Templates and Parameters
If you're going to transform multiple files, it's often better to precompile the Transformer in order to improve performance. One way of doing this is through the use of the Templates object.
Creating a Template
In TrAX, creating a template is an intermediate step between the TransformerFactory and the Transformer. As shown in Listing 10.8a, the Templates object actually creates the Transformer, rather than the TransformerFactory.
Listing 10.8a Using Templates in Java
import javax.xml.transform.stream.StreamSource; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.TransformerFactory; import javax.xml.transform.Source; import javax.xml.transform.Transformer; import javax.xml.transform.Templates; public class TransformFile extends Object { public static void main (String args[]) throws Exception { String XMLFileName = "events.xml"; String OutputFileName = "transform.html"; StreamSource source = new StreamSource(XMLFileName); StreamResult result = new StreamResult(OutputFileName); TransformerFactory transFactory = TransformerFactory.newInstance(); Source style=transFactory.getAssociatedStylesheet(source, null, null, null); Templates template = transFactory.newTemplates(style); Transformer trans = template.newTransformer(); trans.transform(source, result); } }
Templates are most helpful when you intend to do more than one transformation using the same style sheet. Often this involves the use of a parameter, as described in the next section.
C++
Templates are easy to use in C++. In order to use a template, you must declare the XSLT style sheet as a FreeThreadedDOMDocument object. You must also create an IXSLProcessor object that will perform the actual transformation of the XML document. To complete the transformation, load the XML document into the processor and then use the transform method to complete the transformation. The get_output method will retrieve the resulting HTML string, as shown in Listing 10.8b.
Listing 10.8b Using Templates in C++
... CComPtr<MSXML2::IXMLDOMDocument> pXSLTDoc; CComPtr<MSXML2::IXSLTemplate> pXSLTTemplate; CComPtr<MSXML2::IXSLProcessor> pXSLTProcessor; CComVariant varOutput; pXMLDoc.CoCreateInstance(__uuidof(MSXML2::DOMDocument)); pXSLTDoc.CoCreateInstance(__uuidof(MSXML2::FreeThreadedDOMDocument)); pXSLTTemplate.CoCreateInstance(__uuidof(MSXML2::XSLTemplate)); pXMLDoc->load("events.xml"); pXSLTDoc->load("style.xsl"); pXSLTTemplate->putref_stylesheet(pXSLTDoc); pXSLTProcessor = pXSLTTemplate->createProcessor(); pXSLTProcessor->put_input(CComVariant(pXMLDoc)); pXSLTProcessor->transform(); pXSLTProcessor->get_output(&varOutput); wprintf(L"%s", V_BSTR(&varOutput)); } ...
Visual Basic .NET
Templates are also easy to use in Visual Basic. In order to use a template, you must declare the XSLT style sheet as a FreeThreadedDOMDocument40 object. You must also create an IXSLProcessor object that will perform the actual transformation of the XML document. To complete the transformation, load the XML document into the processor and then use the transform method to complete the transformation, as shown in Listing 10.8c. The output property contains the resulting HTML.
Listing 10.8c Using Templates in Visual Basic .NET
... Dim XMLDoc As New MSXML2.DOMDocument40() Dim XSLTDoc As New MSXML2.FreeThreadedDOMDocument40() Dim XSLTTemplate As New MSXML2.XSLTemplate40() Dim XSLTProcessor As MSXML2.IXSLProcessor 'Load XMLDoc XMLDoc.async = False XMLDoc.load("events.xml") 'Load XSLTDoc XSLTDoc.async = False XSLTDoc.load("style.xsl") 'Load XSLTTemplate with stylesheet XSLTTemplate.stylesheet = XSLTDoc 'Create processor and then perform transformation XSLTProcessor = XSLTTemplate.createProcessor() XSLTProcessor.input = XMLDoc XSLTProcessor.transform() MsgBox(XSLTProcessor.output) End Sub
PHP
There's no means of reusing a style sheet without reparsing it in PHP's Sablotron interface.
Perl
As discussed in the text that introduced Listing 10.6e, you can use XML::Sablotron::DOM and XML::Sablotron's addAgrTree() method to obtain the same function result as you'd get by using TrAX-style templates. (See Listing 10.10c in the section titled "Transforming Multiple Files" for an example.)
Using a Parameter
As mentioned in Chapter 9, parameters are values you can pass in when executing the transformation. These values can then affect the output. In Listing 9.43, we had a parameter, optionalChoice, that determined what information would be displayed and how.
We can also pass parameters into the transformation programmatically (as opposed to passing parameters from the command line) using the setParameter() method on the Transformer object, as shown in Listing 10.9a.
Listing 10.9a Setting a Parameter in Java
... Templates template = transFactory.newTemplates(style); Transformer trans = template.newTransformer(); trans.setParameter("optionalChoice", "yes"); trans.transform(source, result); } }
With the parameter set, the output is affected accordingly, as shown in Figure 10.2.
Figure 10.2 Adding the parameter affects the final output.
C++ and Visual Basic .NET
InVisual Basic .NET MSXML's implementation, parameters are embedded into the XSLT style sheet using the <xsl: param> tag. For example, if we wanted to implement the optionalChoice parameter, it would look like this:
<xsl:param name="optionalChoice">yes</xsl:param> ... optionalChoice = <xsl:value-of select="$optionalChoice"/>
There is no mechanism for passing parameters programmatically.
PHP
You can pass parameters to the XSLT processor with PHP's xslt_process() command as a hash, as shown in Listing 10.9b.
Listing 10.9b Setting a Parameter in PHP
<?php ... $args = array ( '/_xml' => $xml ); $params = array ( 'optionalChoice' => 'yes' ); $transformed = xslt_process($xh, 'arg:/_xml', $xsl, NULL, $args, $params); ... ?>
Perl
As with PHP's Sablotron interface, you can pass parameters directly to the process command. But using XML::Sablotron, you can also add parameters before processing using the addParam() method, as shown in Listing 10.9c.
Listing 10.9c Setting a Parameter in Perl
... eval { $xsl->addArgTree( $sit, 'source', $doc ); $xsl->addArgTree( $sit, 'sheet', $style ); $xsl->addParam( $sit, 'optionalChoice', 'yes' ); $xsl->process( $sit, 'arg:/sheet', 'arg:/source', 'arg:/result' ); my $result = $xsl->getResultArg('arg:/result'); print $result; }; ...
Transforming Multiple Files
With templates and parameters, we can efficiently transform multiple files or transform the same file multiple times, as shown in Listing 10.10a.
Listing 10.10a Multiple Transformations in Java
... public class TransformFile extends Object { public static void main (String args[]) throws Exception { String XMLFileName = "events.xml"; String OutputFileName_opt = "transform_opt.html"; String OutputFileName_mand = "transform_mand.html"; StreamSource source = new StreamSource(XMLFileName); StreamResult result_opt = new StreamResult(OutputFileName_opt); StreamResult result_mand = new StreamResult(OutputFileName_mand); TransformerFactory transFactory = TransformerFactory.newInstance(); Source style=transFactory.getAssociatedStylesheet(source, null, null, null); Templates template = transFactory.newTemplates(style); Transformer trans = template.newTransformer(); trans.setParameter("optionalChoice", "yes"); trans.transform(source, result_opt); trans.setParameter("optionalChoice", "no"); trans.transform(source, result_mand); } }
You can transform as many files as you like. As long as you're using the same transformer, they'll all use the same style sheet. However, you can change the source, or in this case, the result.
Perl
Processing the same file multiple times without re-parsing it is easy. The only caveat is that the argument list is flushed each time you execute the process() method; you have to add all arguments each time you process, even if they haven't changed. In Listing 10.10b, the only change will be the parameter optionalChoice, but we could just as easily apply a new style sheet to the source document, or parse a new source with the same style sheet, by inserting the appropriate addArgTree() calls.
Listing 10.10b Multiple Transformations in Perl
... eval { $xsl->addArgTree( $sit, 'source', $doc ); $xsl->addArgTree( $sit, 'style', $style ); $xsl->addParam( $sit, 'optionalChoice', 'yes' ); $xsl->process( $sit, 'arg:/style', 'arg:/source', 'arg:/result' ); my $result = $xsl->getResultArg('arg:/result'); print $result; $xsl->addArgTree( $sit, 'source', $doc ); $xsl->addArgTree( $sit, 'style', $style ); $xsl->addParam( $sit, 'optionalChoice', 'no' ); $xsl->process( $sit, 'arg:/style', 'arg:/source', 'arg:/result2' ); my $result2 = $xsl->getResultArg('arg:/result2'); print $result2; }; ...