XSLT transformation of files

This example shows using Xalan to transform a source XML document into a target document using either a stylesheet document or an embeded stylesheet processing instruction. The input document is the books.xml document from another example. The stylesheet document sortbooks.xslt contains this stylesheet which will output the same document as an HTML table sorted by title

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output encoding="UTF-8" method="html" />
<xsl:template match="/">
<html><head>
<title>Books</title>
</head>
<body>
<h1>Books by title</h1>
<table border='1'>
<xsl:for-each select="//library/book">
<xsl:sort select="title"/>
<tr><td><xsl:value-of select="title"/></td><td><xsl:value-of select="author"/></td></tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>

First create a Xalan object and then instantiate the transformer object. Note that this object owns all the memory.

DIM OBJECT x, OBJECT t, rc
OBJECT x = CREATE "dynamic", "dynxalan"
OBJECT t = x.CreateTransformer()
TRY
	rc = t.TransformFile("books.xml", "sortbooks.xslt", "catalog.html")
CATCH
        rc = -1
END TRY
IF (rc <> 0)
	PRINT rc, $ERR
	PRINT t.LastError$
END IF

Some XML documents, e.g. KCML panic files, have embedded stylesheet processing instructions that reference a separate stylesheet. These documents can be processed by a variant on the above method

rc = t.TransformFiles("panic1234.xml", "panic1234.html")

A more general approach to parsing files is to convert them to input objects and use the more general transform method e.g.

DIM OBJECT x, OBJECT t, rc
DIM OBJECT oIn, OBJECT oOut, OBJECT oXslt
OBJECT x = CREATE "dynamic", "dynxalan"
OBJECT t = x.CreateTransformer()
OBJECT oIn = t.InputSourceFile("books.xml")
OBJECT oXslt = t.InputSourceFile("sortbooks.xslt")
OBJECT oOut = t.ResultTargetFile("catalog.htm")
TRY
	rc = t.transform(OBJECT oIn, OBJECT oXslt, OBJECT oOut)
CATCH
	rc = -1
END TRY
IF (rc <> 0)
	PRINT rc, $ERR
	PRINT t.LastError$
END IF
OBJECT oIn = NULL
OBJECT oXslt= NULL
OBJECT oOut = NULL
OBJECT t = NULL
OBJECT x = NULL

The constructor for the XSLTInputSource object will open the file and read it into a stream object. The file must exist. Similarly the constructor for the XSLTResultTarget object will either create a new target file or truncate an existing one. The file will receive the output stream from the transform. Again there is a variant of transform() that takes just an input object and a target object for input documents that have embedded stylesheet PIs.

Output to string

You don't have to output the result of the transform to a file. It is possible to output to a string. This uses the DOMStringPrintWriter object. You create a blank string that you wish to fill. Then create a DOMStringPrintWriter that handles the actual writing to this string. An XSLTResultTarget can then be created from the Writer. Instead of XSLTResultTarget outputting to a stream the output is handled by the Writer which prints it into the string.

The output string will always be encoded in UTF-8 but Xalan actually generated it in UTF-16 and KCML converted it into its native encoding. Unfortunately Xalan will insert a META tag into html output, or an encoding attribute into the XML declaration for xml output, stating the encoding is UTF-16 unless you specify an <xsl:output encoding="UTF-8" /> tag in the XSLT. Xalan will try to guess the output method in the absence of an xsl:output tag but explicitly specifying both the output type and encoding is best practice anyway.

DIM OBJECT x, OBJECT t, rc
DIM OBJECT oIn, OBJECT oOut, OBJECT oXslt
DIM OBJECT oString, OBJECT oWriter, str$0 
OBJECT x = CREATE "dynamic", "dynxalan"
OBJECT t = x.CreateTransformer()
OBJECT oIn = t.InputSourceFile("books.xml")
OBJECT oXslt = t.InputSourceFile("sortbooks.xslt")
OBJECT oString = t.XalanString()
OBJECT oWriter = t.StringWriter(OBJECT oString)
OBJECT oOut = t.ResultTargetWriter(OBJECT oWriter)
TRY
	rc = t.transform(OBJECT oIn, OBJECT oXslt, OBJECT oOut)
CATCH
	rc = -1
END TRY
IF (rc <> 0)
	PRINT rc, $ERR
	PRINT t.LastError$
END IF
REDIM str$ = oString.str$()
OBJECT oIn = NULL
OBJECT oXslt= NULL
OBJECT oOut = NULL
OBJECT oString = NULL
OBJECT oWriter = NULL
OBJECT t = NULL
OBJECT x = NULL

Output to Xerces DOM

You can also construct an Xerces DOM object from the output. We will need to create a document to fill in.

DIM OBJECT x, OBJECT t, rc
DIM OBJECT oIn, OBJECT oOut, OBJECT oXslt
DIM OBJECT oImpl, OBJECT oDoc, OBJECT oElement, OBJECT oFormatter
OBJECT x = CREATE "dynamic", "dynxalan"
OBJECT t = x.CreateTransformer()
OBJECT oIn = t.InputSourceFile("books.xml")
OBJECT oXslt = t.InputSourceFile("sortbooks.xslt")
OBJECT oImpl = x.getDOMImplementation("LS")
OBJECT oDoc = oImpl.createDocument(OBJECT NULL, "root", OBJECT NULL)
OBJECT oElement = oDoc.DocumentElement
OBJECT oFormatter = t.FormatterToXercesDOM(OBJECT oDoc, OBJECT oElement)
OBJECT oOut = t.ResultTargetFormatter(OBJECT oFormatter)
TRY
	rc = t.transform(OBJECT oIn, OBJECT oXslt, OBJECT oOut)
CATCH
	rc = -1
END TRY
IF (rc <> 0)
	PRINT rc, $ERR
	PRINT t.LastError$
END IF
// The DOM Document oDoc is now full
OBJECT oIn = NULL
OBJECT oXslt= NULL
OBJECT oOut = NULL
OBJECT oImpl = NULL
OBJECT oDoc = NULL
OBJECT oElement = NULL
OBJECT oFormatter = NULL
OBJECT t = NULL
OBJECT x = NULL

For other XSLT examples click here.