Performing XSLT Transformations
XSLT (Extensible Stylesheet Language Transformations) is an XML-based language that you use to describe how to transform a given XML document into another XML or other human-readable document. You can use classes in the %XML.XSLT and %XML.XSLT2 packages to perform XSLT 1.0 and 2.0 transforms.
The XML declaration of any XML document that you use should indicate the character encoding of that document, and the document should be encoded as declared. If the character encoding is not declared, InterSystems IRIS® data platform uses the defaults described in Character Encoding of Input and Output. If these defaults are not correct, modify the XML declaration so that it specifies the character set actually used.
Overview of Performing XSLT Transformations in InterSystems IRIS
InterSystems IRIS provides two XSLT processors, each with its own API:
-
The Xalan processor supports XSLT 1.0. The %XML.XSLT package provides the API for this processor.
-
The Saxon processor supports XSLT 2.0. The %XML.XSLT2 package provides the API for this processor.
The %XML.XSLT2 API sends requests to Saxon over a connection to the XSLT 2.0 Gateway. The gateway allows multiple connections. This means that, for example, you could have two separate InterSystems IRIS processes connected to the gateway, each with its own set of compiled stylesheets, sending transform requests at the same time.
With the Saxon processor, compiled stylesheets and the isc:evaluate cache are connection-specific; you must manage your own connection to take advantage of either feature. If you open a connection and create a compiled stylesheet or evaluate a transform that populates the isc:evaluate cache, all other transforms evaluated on that connection will have access to the compiled stylesheet and isc:evaluate cache entries. If you open a new connection, other connections (and their compiled stylesheets and caches) are ignored.
The APIs are similar for the two processors, except that methods in %XML.XSLT2 use an additional argument, to specify the gateway connection to use.
To perform XSLT transformations, do the following:
-
If you are using the Saxon processor, configure the XSLT Gateway Server, as described in the next section. Or use the default configuration.
The gateway is not needed if you are using the Xalan processor.
The system automatically starts the gateway when needed. Or you can manually start it.
-
If you are using the Saxon processor, optionally create an instance of %Net.Remote.GatewayOpens in a new tab, which represents a single connection to the XSLT Gateway.
Note that this step is required to take advantage of compiled stylesheets and the isc:evaluate cache, when you are using the Saxon processor.
-
Optionally create a compiled stylesheet and load it into memory. See Creating a Compiled Stylesheet. If you are using the Saxon processor, be sure to specify the gateway argument when you create the compiled stylesheet.
This step is useful if you intend to use the same stylesheet repeatedly. This step, however, also consumes memory. Be sure to remove the compiled stylesheet when you no longer need it.
-
Call one of the transform methods of the applicable API. If you are using the Saxon processor, optionally specify the gateway argument when you call the transform method.
-
Optionally call additional transform methods. If you are using the Saxon processor, optionally specify the gateway argument when you call the transform method; this enables you to evaluate another transform using the same connection. This transformation will have access to all compiled stylesheets and isc:evaluate cache entries associated with this connection. If you open a new connection, other connections (and their compiled stylesheets and caches) are ignored.
Starting, Stopping, and Configuring the XSLT 2.0 Gateway
When you use the Saxon processor (to perform XSLT 2.0 transformations), InterSystems IRIS automatically starts and uses the XSLT 2.0 Gateway (%XSLT Server). This gateway is configured in the same manner as other external server connections; see Managing External Server Connections for details. The XSLT 2.0 Gateway is listed as %XSLT Server.
Reusing an XSLT Gateway Server Connection (XSLT 2.0)
When you use the Saxon processor, InterSystems IRIS uses the XSLT 2.0 Gateway, which you have previously configured. To communicate with this gateway, InterSystems IRIS internally creates an XSLT gateway connection (an instance of %Net.Remote.GatewayOpens in a new tab). By default, the system creates a connection, uses it for the transformation, and then discards the connection. There is overhead associated with opening a new connection, so maintaining a single connection for multiple transforms gives the best performance. Also, you must maintain your own connection in order to take advantage of compiled stylesheets and the isc:evaluate cache.
To reuse an XSLT gateway connection:
-
Call the StartGateway() method of %XML.XSLT2.TransformerOpens in a new tab:
set status=##class(%XML.XSLT2.Transformer).StartGateway(.gateway)
This method starts the XSLT 2.0 Gateway (if it is not already running) and returns, as output, an instance of %Net.Remote.GatewayOpens in a new tab. Note that the method also returns a status.
The instance of %Net.Remote.GatewayOpens in a new tab represents the connection to the gateway.
StartGateway() has an optional second argument, useSharedMemory. If this argument is true (the default), connections to localhost or to 127.0.0.1 will use shared memory, if that is possible. To force the connections to only use TCP/IP, set this argument to false.
-
Check the status returned by the previous step:
if $$$ISERR(status) { quit }
-
Create any compiled stylesheets. When you do so, specify the gateway argument as the instance of instance of %Net.Remote.GatewayOpens in a new tab that you created in step 1.
-
Call the transform methods of %XML.XSLT2.TransformerOpens in a new tab as needed (TransformFile(), TransformFileWithCompiledXSL(), TransformStream(), and TransformStreamWithCompiledXSL()). When you do so, specify the gateway argument as the instance of %Net.Remote.GatewayOpens in a new tab that you created in step 1.
-
When you no longer need a given compiled stylesheet, call the ReleaseFromServer() method of %XML.XSLT2.CompiledStyleSheetOpens in a new tab:
Set status=##class(%XML.XSLT2.CompiledStyleSheet).ReleaseFromServer(compiledStyleSheet,,gateway)
Important:Be sure to use this method when you no longer need the compiled stylesheet.
-
When you no longer need the XSLT gateway connection, call the StopGateway() method of %XML.XSLT2.TransformerOpens in a new tab, passing the gateway connection as the argument:
set status=##class(%XML.XSLT2.Transformer).StopGateway(gateway)
This method discards the connection and resets the current device. It does not stop the XSLT 2.0 Gateway.
Important:Be sure to use this method when you no longer need the connection.
Troubleshooting an XSLT 2.0 Gateway Server Connection
While the XSLT 2.0 gateway is open, it is possible for a connection between InterSystems IRIS and the gateway server to become invalid. For example, if a network error occurs or the gateway server is restarted after InterSystems IRIS connects to it, a connection may not close properly. As a result, you may experience errors.
You can attempt to reconnect InterSystems IRIS to the gateway server by calling the %LostConnectionCleanup() method and %Reconnect methods of the XSLT gateway connection objectOpens in a new tab in succession. For more information, see %Net.Remote.GatewayOpens in a new tabOpens in a new tab, which is the class that the XSLT gateway connection object inherits from.
If you prefer to automate the process of reconnecting to the gateway server in the event of a disconnection, set the AttemptReconnect property for the gateway connection object to true.
Creating a Compiled Stylesheet
If you intend to use the same style sheet repeatedly, you may want to compile it to improve speed. Note that this step consumes memory. Be sure to remove the compiled style sheet when you no longer need it.
To create a compiled style sheet:
-
If you are using the Xalan processor (for XSLT 1.0), use one of the following class methods of %XML.XSLT.CompiledStyleSheetOpens in a new tab:
-
CreateFromFile()
-
CreateFromStream()
-
-
If you are using the Saxon processor (for XSLT 2.0), use one of the following class methods of %XML.XSLT2.CompiledStyleSheetOpens in a new tab:
-
CreateFromFile()
-
CreateFromStream()
Also note that you will need to create an XSLT Gateway connection; see Reusing an XSLT Gateway Server Connection (XSLT 2.0).
-
For all these methods, the complete argument list is as follows, in order:
-
source — The style sheet.
For CreateFromFile(), this argument is the filename. For CreateFromStream(), this argument is a stream.
-
compiledStyleSheet — The compiled style sheet, returned as an output parameter.
This is an instance of the style sheet class (%XML.XSLT.CompiledStyleSheetOpens in a new tab or %XML.XSLT2.CompiledStyleSheetOpens in a new tab, as appropriate).
-
errorHandler — An optional custom error handler to use when compiling the style sheet. See Customizing the Error Handling.
For methods in both classes, this is an instance of %XML.XSLT.ErrorHandlerOpens in a new tab.
-
(Only for %XML.XSLT2.CompiledStyleSheetOpens in a new tab) gateway — An instance of %Net.Remote.GatewayOpens in a new tab. See Reusing an XSLT Gateway Server Connection (XSLT 2.0).
The CreateFromFile() and CreateFromStream() methods return a status, which should be checked.
For example:
//set tXSL equal to the OREF of a suitable stream
Set tSC=##class(%XML.XSLT.CompiledStyleSheet).CreateFromStream(tXSL,.tCompiledStyleSheet)
If $$$ISERR(tSC) Quit
Performing an XSLT Transform
To perform an XSLT transform:
-
If you are using the Xalan processor (for XSLT 1.0), use one of the following class methods of %XML.XSLT.TransformerOpens in a new tab:
-
TransformFile() — Transforms a file, given an XSLT style sheet.
-
TransformFileWithCompiledXSL() — Transforms a file, given a compiled XSLT style sheet.
-
TransformStream() — Transforms a stream, given an XSLT style sheet.
-
TransformStreamWithCompiledXSL() — Transforms a stream, given a compiled XSLT style sheet.
-
TransformStringWithCompiledXSL() — Transforms a string, given a compiled XSLT style sheet.
For these methods, use an instance of %XML.XSLT.CompiledStyleSheetOpens in a new tab as the compiled stylesheet. See Creating a Compiled Stylesheet.
-
-
If you are using the Saxon processor (for XSLT 2.0), use one of the following class methods of %XML.XSLT2.TransformerOpens in a new tab:
-
TransformFile() — Transforms a file, given an XSLT style sheet.
-
TransformFileWithCompiledXSL() — Transforms a file, given a compiled XSLT style sheet.
-
TransformStream() — Transforms a stream, given an XSLT style sheet.
-
TransformStreamWithCompiledXSL() — Transforms a stream, given a compiled XSLT style sheet.
For these methods, use an instance of %XML.XSLT2.CompiledStyleSheetOpens in a new tab as the compiled stylesheet. See Creating a Compiled Stylesheet.
-
These methods have similar signatures. The argument lists for these methods are as follows, in order:
-
pSource — The source XML, which is to be transformed. See the table after this list.
-
pXSL — The style sheet or compiled style sheet. See the table after this list.
-
pOutput — The resulting XML, returned as an output parameter. See the table after this list.
-
pErrorHandler — An optional custom error handler. See Customizing the Error Handling. If you do not specify a custom error handler, the method uses a new instance of %XML.XSLT.ErrorHandlerOpens in a new tab (for both classes).
-
pParms — An optional InterSystems IRIS multidimensional array that contains parameters to be passed to the style sheet. See Specifying Parameters for Use by the Stylesheet.
-
pCallbackHandler — An optional callback handler that defines XSLT extension functions. See Adding and Using XSLT Extension Functions.
-
pResolver — An optional entity resolver. See Performing Custom Entity Resolution.
(Only for %XML.XSLT2.TransformerOpens in a new tab) gateway — An optional instance of %Net.Remote.GatewayOpens in a new tab. Specify this argument if you want to reuse an XSLT Gateway connection for better performance; see Reusing an XSLT Gateway Server Connection (XSLT 2.0).
For reference, the following table shows the first three arguments of these methods, compared side by side:
Method | pSource (Input XML) | pXSL (Stylesheet) | pOutput(Output XML) |
---|---|---|---|
TransformFile() | String that gives a file name | String that gives a file name | String that gives a file name |
TransformFileWithCompiledXSL() | String that gives a file name | Compiled style sheet | String that gives a file name |
TransformStream() | Stream | Stream | Stream, returned by reference |
TransformStreamWithCompiledXSL() | Stream | Compiled style sheet | Stream, returned by reference |
TransformStringWithCompiledXSL() | String | Compiled style sheet | String that gives a file name |
Examples
This section shows a couple of transformations, using the following code (but different input files):
Set in="c:\0test\xslt-example-input.xml"
Set xsl="c:\0test\xslt-example-stylesheet.xsl"
Set out="c:\0test\xslt-example-output.xml"
Set tSC=##class(%XML.XSLT.Transformer).TransformFile(in,xsl,.out)
Write tSC
Example 1: Simple Substitution
In this example, we start with the following input XML:
<?xml version="1.0" ?>
<s1 title="s1 title attr">
<s2 title="s2 title attr">
<s3 title="s3 title attr">Content</s3>
</s2>
</s1>
And we use the following style sheet:
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="//@* | //node()">
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:apply-templates select="node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/s1/s2/s3">
<xsl:apply-templates select="@*"/>
<xsl:copy>
Content Replaced
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
In this case, the output file would be as follows:
<?xml version="1.0" encoding="UTF-8"?>
<s1 title="s1 title attr">
<s2 title="s2 title attr">
<s3>
Content Replaced
</s3>
</s2>
</s1>
Example 2: Extraction of Contents
In this example, we start with the following input XML:
<?xml version="1.0" encoding="UTF-8"?>
<MyRoot>
<MyElement No="13">Some text</MyElement>
<MyElement No="14">Some more text</MyElement>
</MyRoot>
And we use the following style sheet:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.1"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output method="text"
media-type="text/plain"/>
<xsl:strip-space elements="*"/>
<!-- utilities not associated with specific tags -->
<!-- emit a newline -->
<xsl:template name="NL">
<xsl:text>
</xsl:text>
</xsl:template>
<!-- beginning of processing -->
<xsl:template match="/">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="MyElement">
<xsl:value-of select="@No"/>
<xsl:text>: </xsl:text>
<xsl:value-of select="."/>
<xsl:call-template name="NL"/>
</xsl:template>
</xsl:stylesheet>
In this case, the output file would be as follows:
13: Some text
14: Some more text
Additional Examples
For XSLT 1.0, see the Example(), Example2(), and other methods in %XML.XSLT.TransformerOpens in a new tab.
Customizing the Error Handling
When an error occurs, either XSLT processor (Xalan or Saxon) executes the error() method of the current error handler, sending a message as an argument to that method. Similarly, when a fatal error or warning occurs, the XSLT processor executes the fatalError() or warning() method, as appropriate.
For all three of these methods, the default behavior is to write the message to the current device.
To customize the error handling, you do the following:
-
For either the Xalan or the Saxon processor, create a subclass of %XML.XSLT.ErrorHandlerOpens in a new tab. In this subclass, implement the error(), fatalError(), and warning() methods as needed.
Each of these methods accepts a single argument, a string that contains the message sent by the XSLT processor.
These methods do not return values.
-
Then:
-
To use this error handler when compiling a stylesheet, create an instance of your subclass and use it in the argument list when you compile the stylesheet. See Creating a Compiled Stylesheet.
-
To use this error handler when performing an XSLT transform, create an instance of your subclass and use it in the argument list of the transform method you use. See Performing an XSLT Transform.
-
Specifying Parameters for Use by the Stylesheet
To specify parameters for use by the stylesheet:
-
Create an instance of %ArrayOfDataTypesOpens in a new tab.
-
Call the SetAt() method of this instance to add parameters and their values to this instance. For SetAt(), specify the first argument as the parameter value and the second argument as the parameter name.
Add as many parameters as needed.
For example:
Set tParameters=##class(%ArrayOfDataTypes).%New() Set tSC=tParameters.SetAt(1,"myparameter") Set tSC=tParameters.SetAt(2,"anotherparameter")
-
Use this instance as the pParms argument of the transform method.
Instead of an %ArrayOfDataTypesOpens in a new tab, you can use an InterSystems IRIS multidimensional array, which can have any number of nodes with following structure and value:
Node | Value |
---|---|
arrayname("parameter_name") | Value of the parameter named by parameter_name |
Adding and Using XSLT Extension Functions
You can create XSLT extension functions in InterSystems IRIS and then use them within your stylesheet, as follows:
-
For XSLT 2.0 (the Saxon processor), you can use the evaluate function in the namespace com.intersystems.xsltgateway.XSLTGateway or the evaluate function in the namespace http://extension-functions.intersystems.com
-
For XSLT 1.0 (the Xalan processor), you can only use the evaluate function in the namespace http://extension-functions.intersystems.com
By default (and as an example), the latter function reverses the characters that it receives. Typically, however, the default behavior is not used, because you implement some other behavior. To simulate multiple separate functions, you pass a selector as the first argument and implement a switch that uses that value to choose the processing to perform.
Internally, the evaluate function is implemented as a method (evaluate()) in the XSLT callback handler.
To add and use XSLT extension functions, do the following:
-
For either the Xalan or the Saxon processor, create a subclass of %XML.XSLT.CallbackHandlerOpens in a new tab. In this subclass, implement the evaluate() method as needed. See the following subsection.
-
In the style sheet, declare the namespace to which the evaluate function belongs and use the evaluate function as needed. See the following subsection.
-
When performing an XSLT transform, create an instance of your subclass and use it in the argument list of the transform method you use. See Performing an XSLT Transform.
Implementing the evaluate() Method
Internally, the code that calls the XSLT processor can pass any number of positional arguments to the evaluate() method of the current callback handler, which receives them as an array that has the following structure:
Node | Value |
---|---|
Args | Number of arguments |
Args(index) | Value of the argument in the position index |
The method has a single return value. The return value can be either:
-
A scalar variable (such as a string or number).
-
A stream object. This allows you to return an extremely long string, one that exceeds the string length limit. The stream has to be wrapped in an instance of %XML.XSLT.StreamAdapterOpens in a new tab which enables the XSLT processor to read the stream. The following shows a partial example:
Method evaluate(Args...) As %String { //create stream ///... // create instance of %XML.XSLT.StreamAdapter to // contain the stream Set return=##class(%XML.XSLT.StreamAdapter).%New(tStream) Quit return }
Using evaluate in a Stylesheet
To use XSLT extension functions in an XSLT, you must declare the namespace of the extension functions in the XSLT stylesheet. For the InterSystems evaluate function, this namespace is http://extension-functions.intersystems.com or com.intersystems.xsltgateway.XSLTGateway, as discussed previously.
The following example shows a style sheet that uses evaluate:
<?xml version="1.0"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
xmlns:isc="http://extension-functions.intersystems.com">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="//@* | //node()">
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:apply-templates select="node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/s1/s2/s3">
<xsl:apply-templates select="@*"/>
<xsl:choose>
<xsl:when test="function-available('isc:evaluate')">
<xsl:copy>
<xsl:value-of select="isc:evaluate(.)" disable-output-escaping="yes"/>
</xsl:copy>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="."/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
For a closer look at this example, see the source code for the Example3() method of %XML.XSLT.TransformerOpens in a new tab.
Working with the isc:evaluate Cache
The XSLT 2.0 gateway caches evaluate function calls in the isc:evaluate cache. The default maximum size of the cache is 1000 items, but you can set the size to a different value. Also, you can clear the cache, you can dump the cache, and you can pre-populate the cache from a %ListOpens in a new tab with the following format:
-
Total number of cache entries
-
For each entry:
-
Total number of evaluate arguments
-
All evaluate arguments
-
Evaluate value
-
The cache also includes a filter list of function names that can be cached. Note the following:
-
Function names can be added to or removed from the filter list.
-
The filter list can be cleared.
-
The filter list can be overridden by setting a boolean that will cache every evaluate call.
Adding a function name to the filter list does not limit the size of the evaluate cache. There may be any number of calls to the same function, but with different arguments and return values. Each combination of function name and arguments is a separate entry in the evaluate cache.
You can use methods from the %XML.XSLT2.TransformerOpens in a new tab class to manipulate the evaluate cache. For details, see the class reference.