Processing XSL
Since JAXP 1.1, JAXP has been the Java API for XML Processing; this replaces the 1.0 version, which was the Java API for XML Parsing. Much of this change is due to the addition of TrAX (yes, it's an API-happy world). Via TrAX, JAXP offers vendor-neutral XML document transformations. This is a welcome feature, as XSL processors have even greater variance across vendors than their XML parser counterparts. Thanks to the JAXP expert groupand in particular Scott Boag and Michael Key, two XSL processor gurusJAXP and TrAX offer a wide array of features and options, and provide complete support for almost all XML transformations. All this is sheltered under the javax.xml.transform package (and a few subpackages); shows the complete set of JAXP/TrAX classes and interfaces (omitting subpackage class definitions).
There are many classes in TrAX, but you typically use only one or two

Like the parsing portion of JAXP, performing XML transformations requires just a few basic steps:
- Obtain a TRansformerFactory.
- Retrieve a TRanformer.
- Perform transformations on XML documents.
The XML transformation process looks a lot like the SAX and DOM parsing process, involving a transformation factory

Basic Transformations
For XML transformations, the factory you want is javax.xml.transform.TransformerFactory. This class is analogous to SAXParserFactory and DocumentBuilderFactory, both of which you've already seen. Obtaining a factory instance is a piece of cake:
TransformerFactory factory = TransformerFactory.newInstance( );
Once you've got the factory, you can set a number of options:
Error listeners ( setErrorListener( )/getErrorListener( ) )
-
Defined in the javax.xml.transform package, the ErrorListener interface allows problems in transformations to be caught and handled programmatically.
URI resolvers ( setURIResolver( )/getURIResolver( ) )
-
The URIResolver interface (also in javax.xml.transform) works just like an EntityResolver in SAXit allows you to override normal resolution of URIs (and entities in an XSL stylesheet).
Factory/transformer features ( setFeature( )/getFeature( ) )
-
Like features on factories in SAX and DOM, these allow you to request certain options be turned on (TRue) or off (false), and affect the factory and all of the TRansformer instances created by that factory.
Passthrough attributes ( setAttribute( )/getAttribute( ) )
-
Attributes in XSL are essentially the same as properties in SAX. They allow you to request specific options not available through JAXP, and are identified by a URI. These are different from features (which accept TRue/false values, and are either on or off), in that they accept an Object as their value.
|
Setting an ErrorListener is one of the most common options you'll use for XML transformations. Defined in the javax.xml.transform.ErrorListener interface, an ErrorListener allows problems in transformation to be caught and handled programmatically. If this sounds like org.xml.sax.ErrorHandler , it is very similar, as evidenced by .
ErrorListener is the TrAX counterpart to SAX's ErrorHandler, and even uses the same three callback methods

Creating an implementation of this interface, filling the three callback methods, and using the setErrorListener( ) method on the TransformerFactory instance you are working with sets your transformation code up to deal with any errors that occur during transformation. In the same vein, a method is provided to set and retrieve the URI resolver for the instances generated by the factory. The interface defined in javax.xml.transform.URIResolver behaves similarly to its SAX counterpart, org.xml.sax.EntityResolver (see ).
Like the relationship between ErrorListener and ErrorHandler, URIResolver mimics the functionality of EntityResolver, albeit with a slightly different method name

This interface, when implemented, allows URIs found in XSL constructs like xsl:import and xsl:include to be handled. Returning a Source (covered next in "Creating a transformer"), you can instruct your transformer to search for the specified document in various locations when a particular URI is encountered. For example, when an include of the URI http://www.headfirstlabs.com/headfirst.xsl is encountered, you might instead return the local document headfirst-color.xsl. Once you set the options of your choice, you can obtain an instance, or instances, of a transformer through the newTransformer( ) method:
// Get the factory TransformerFactory factory = TransformerFactory.newInstance( ); // Configure the factory factory.setErrorResolver(myErrorResolver); factory.setURIResolver(myURIResolver); // Get a Transformer to work with, with the options specified Transformer transformer = factory.newTransformer(new StreamSource("foundation.xsl"));
Creating a transformer
Once you have an instance of a transformer, you can go about actually performing XML transformations. This consists of two basic steps:
- Set the XSL stylesheet to use.
- Perform the transformation, specifying the XML document and result target.
The first step is simple (in fact, you already saw this code in the last code sample): a stylesheet can be supplied when obtaining a transformer instance from the factory.
// Get a Transformer to work with, with the options specified Transformer transformer = factory.newTransformer(new StreamSource("foundation.xsl"));
The location of this stylesheet must be specified by providing a javax.xml.transform.Source instance (actually an instance of an implementation of the Source interface) for its location. The Source interface is the means of locating input, be it a stylesheet, document, or other information set. TrAX provides the Source interface and three concrete implementations (all shown in ):
- javax.xml.transform.stream.StreamSource
- javax.xml.transform.dom.DOMSource
- javax.xml.transform.sax.SAXSource
The Source interface and its three concrete implementations all provide for input to the TrAX/JAXP engine

The first of these, StreamSource, reads input from some type of I/O device. Constructors are provided for accepting an InputStream, a Reader, or a String system ID as input. Once created, the StreamSource can be passed to the transformer for use. This will probably be the Source implementation you use most commonly in programs. It's great for reading a document from a network, input stream, user input, or other static representation of XSL stylesheets. The next Source implementation, DOMSource, provides for reading from an existing DOM tree. It provides a constructor for taking in a DOM org.w3c.dom.Node, and will read from that Node when used. This is ideal for supplying an existing DOM tree to a transformation, perhaps if parsing has already occurred and an XML document is already in memory as a DOM structure, or if you've built a DOM tree programmatically. SAXSource provides for reading input from SAX producers. This Source implementation takes either a SAX org.xml.sax.InputSource, or an org.xml.sax.XMLReader as input, and uses the events from these sources. This is ideal for situations in which a SAX content handler is already in use, and callbacks are set up and need to be triggered prior to transformations. Once you've obtained an instance of a transformer (by providing the stylesheet to use through an appropriate Source), you're ready to perform a transformation:
// Get the factory TransformerFactory factory = TransformerFactory.newInstance( ); // Configure the factory factory.setErrorResolver(myErrorResolver); factory.setURIResolver(myURIResolver); // Get a Transformer to work with, with the options specified Transformer transformer = factory.newTransformer(new StreamSource("xsl/dw-document-html-4.0.xsl")); // Perform transformation on myDocument, and print out result transfomer.transform(new StreamSource("ibm-jaxp-article.xml"), new StreamResult("article.html"));
The transform( ) method takes two arguments: a Source implementation and a javax.xml.transform.Result implementation. You should already be seeing the symmetry in how this works and have an idea about the functionality within the Result interface. The Source provides the XML document to be transformed, and the Result provides an output target for the transformation (as seen in ).
Result and its implementations are essentially outbound mirrors of the inbound Source

Like Source, there are three concrete implementations of the Result interface provided with TrAX and JAXP:
- javax.xml.transform.stream.StreamResult
- javax.xml.transform.dom.DOMResult
- javax.xml.transform.sax.SAXResult
The StreamResult class takes an OutputStream, a Java File, a String system ID, or a Writer. DOMResult takes a DOM Node to output the transformation to (presumably as a DOM org.w3c.dom.Document), and SAXResult takes a SAX ContentHandler instance to fire callbacks to, resulting from the transformed XML. All are analogous to their Source counterparts. While the previous example shows transforming from a stream to a stream, any combination of sources and results is possible. Here are a few examples:
// Perform transformation on jordan.xml, and print out result transformer.transform(new StreamSource("jordan.xml"), new StreamResult(System.out)); // Transform from SAX and output results to a DOM Node transformer.transform(new SAXSource(new InputSource("http://www.oracle.com/catalog.xml")), new DOMResult(DocumentBuilder.newDocument( ))); // Transform from DOM and output to a File transformer.transform(new DOMSource(domTree), new StreamResult(new FileOutputStream("results.xml"))); // Use a custom source and result (JDOM) transformer.transform(new org.jdom.transform.JDOMSource(myJdomDocument), new org.jdom.transform.JDOMResult( ));
The Identity Transformation
One of the cooleralbeit lesser-knownfeatures of JAXP is the ability to convert easily from one XML format to another, using the Source and Result interfaces. This is known as the identity transformation, and is a result of performing a transformation without any stylesheet. A TRansformer instance that performs identity transformations is created by calling the newTRansformer( ) method of a TRansformerFactory with no arguments:
Transformer identity = factory.newTransformer( );
identity in this example has no associated XSL stylesheet, so the TRansform( ) method will not change the incoming XML document, at least in terms of its content. However, by providing different Source and Result types, the document will be converted from one format to another:
// Convert from a file to a DOM tree identity.transform( new StreamSource("input.xml"), new DOMResult(DocumentBuilder.newDocument( )));
This is a pretty cool way to quickly convert between different formats. Even more useful, this provides a serialization mechanism:
// Convert a DOM tree to a file identity.transform(new DOMSource(myDocument), new StreamResult("output.xml"));
While this is a great way to quickly get a document from memory onto disk, the identity transformation doesn't offer you much in the way of output options. It's best used as a quick solution, rather than a full-fledged serialization technique.
Caching Transformation Instructions
Despite the simplicity of transformations using JAXP, there are two significant dis-advantages to using a transformer instance directly from your transformerFactory:
- The transformer object processes the XSL stylesheet each and every time that TRansform( ) is executed.
- Instances of transformer are not thread-safeyou can't use the same instances across multiple threads.
To understand the root of these problems, realize that a transformer must reprocess the XSL it uses every time it executes a transformation (via the transform( ) method). Even worse, if this processing is occurring in multiple threads at the same time, you can start to have real problems. And, on top of the threading issues, you've got to pay the processing cost for the XSL stylesheet over and over again. To solve these problems, JAXP provides the javax.xml.transform.Templates object. A Templates object represents a stylesheet, but it precompiles the instructions in the supplied XSL stylesheet. This means that while there is a little up-front cost in using a Templates object (as it precompiles the XSL), there is no repeat cost in processing. Figure 7-11 shows how the Templates object fits into the overall process flow (compare it to ).
Using a Templates object inserts an extra step into the normal JAXP/TrAX workflow process

This is how it translates into actual code:
try { // Set up input documents Source inputXML = new StreamSource( new File(args[0])); Source inputXSL = new StreamSource( new File(args[1])); // Set up output sink Result outputXHTML = new StreamResult( new File("output.html")); // Setup a factory for transforms TransformerFactory factory = TransformerFactory.newInstance( ); // Precompile the XSL into a Templates object Templates templates = factory.newTemplates(inputXSL); // Get a Transformer from the Templates Transformer transformer = templates.newTransformer( ); // Perform the transformation transformer.transform(inputXML, outputXHTML); } catch (TransformerConfigurationException e) { System.out.println("The underlying XSL processor " + "does not support the requested features."); } catch (TransformerException e) { System.out.println("Error occurred obtaining " + "XSL processor."); }
This just required one additional line of code, and one slightly modified line of code (both are bolded). These are the only changes required to move from direct use of a transformer to adding precompilation into the process. So the question becomes when to grab a Transformer directly and when to use Templates objects. When I use XSL, I almost always use the same stylesheet repeatedly. So rather than paying for multiple passes over the XSL, I'd prefer to just precompile the instructions into a Templates object and only pay the cost of processing the XSL instructions once. That said, there are a few cases where it's better to pull a TRansformer straight from your TRansformerFactory. If you know that you're only going to perform a single transformation using a specific stylesheet, then it's faster not to precompile the stylesheet into a Templates object. However, that assumes that you're only using the stylesheet a single time; in my own tests, I find that once I've used an XSL twice, it's a wash between using a Templates object as opposed to requesting a TRansformer directly, and once you use a stylesheet three times, the Templates approach wins hands down. You also need to be sure you're not going to have any threading issues; that's a simple thing to determine, though, so I'll leave that to you to apply in your programming. As a general rule, it's almost always safer to go with the Templates object.
Changing the Processor Implementation
You've already seen that by using the javax.xml.parsers.SAXParserFactory system property, you can specify your own SAX parser implementation (and the same was true for DOM building). This same principle applies for your XSL processor. JAXP comes prepackaged with Xalan-Java (http://xml.apache.org/xalan-j), which is another Apache open source project (and the processing counterpart to Xerces). Xalan-Java is a mature XSL processor and is a good choice for many use-cases. But if you need to use a different XSL processor, JAXP provides a configuration mechanism. To exercise this option, supply a value for the system property named javax.xml.transform.TransformerFactory. You need to assign this property the name of a class to instantiate; that class should extend javax.xml.transform.TransformerFactory and provide implementations for that interface's abstract methods:
java -Djavax.xml.transform.TransformerFactory=net.sf.saxon.TransformerFactoryImpl javaxml3.TransformTester ibm-jaxp-article.xml xsl/dw-document-4.0.xsl
The name of both the property and the class to extend is javax.xml.transform.TransformerFactory. This isn't meant to be confusing, but to serve as a reminder that the value of the property should extend the class of the same name. In this example, the Saxon processor (http://www.saxonica.com) is specified; Saxon's TRansformerFactoryImpl will create Saxon-specific TRansformer instances. With this one line, you've effectively moved from Xalan-Java to Saxon. You could also set this property programmatically:
private void initTransformer( ) { System.setProperty("javax.xml.transform.TransformerFactory", "net.sf.saxon.TransformerFactoryImpl"); // Perform other Transformer setup tasks }