7.5. XSLTAnother specification incorporated into the JAXP API is the XML Stylesheet Transformation (XSLT) system. An XSLT transformation takes an input XML document and transforms it into an output format (not necessarily XML) according to a set of rules specified in an XSL stylesheet. One common application of XSL stylesheets is transforming XML into HTML for presentation in a browser; this is often done in the browser directly or on the web server via a content management system such as the Apache project's Cocoon (http://cocoon.apache.org). The following XSL document converts the orders.xml file from the SAX example into HTML: <?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/ Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format"> <xsl:template match="orders"> <html> <head><title>Order Summary</title></head> <body> <xsl:apply-templates/> </body> </html> </xsl:template> <xsl:template match="order"> <h1>Order Number <xsl:value-of select="@idnumber"/></h1> Ship To: <pre> <xsl:value-of select="shippingaddr"/> </pre> <ul> <xsl:apply-templates select="item"/> </ul> </xsl:template> <xsl:template match="item"> <li><xsl:value-of select="@quantity"/> of item <xsl:value-of select="@idnumber"/><xsl:apply-templates/></li> </xsl:template> <xsl:template match="handling"> <br/>Special Instructions: <xsl:value-of select="."/> </xsl:template> </xsl:stylesheet> The XSL file consists of a series of templates. The XSL processor matches each template to a particular XML tag and replaces the tag with the template contents. For example, when the processor encounters an <orders> and an </orders> tag, it replaces them with: <html> <head><title>Order Summary</title></head> <body> <xsl:apply-templates/> </body> </html> The <xsl:apply-templates/> command tells the XSLT processor to recursively apply all of the available templates to the XML content contained within the <orders> tag pair. Other XSL commands allow the processor to display element attributes, limit recursion to specific templates, and so forth. For more examples, please check one of the books recommended at the beginning of this chapter. After running the orders.xml file through the stylesheet, we get this HTML: <html xmlns:fo="http://www.w3.org/1999/XSL/Format"> <head> <META http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Order Summary</title> </head> <body> <h1>Order Number 3123</h1> Ship To: <pre>One Main St Boston, MA 02112</pre> <ul> <li>13 of item 7231</li> <li>2 of item 1296 <br>Special Instructions: Please embroider in a tasteful manner! </li> </ul> <!-- Snipped for brevity --> </body> </html> The classes in the JAXP javax.xml.transform package allow programs to use XSLT Transformer objects, such as Apache Xalan (which ships with the JAXP reference implementation) to transform XML documents. At this point, we should point out that JAXP doesn't require XSLT at all: instead, it is a generic transformations API that theoretically can be used with a variety of transformation systems. However, XSLT is by far the most popular and widely implemented, so the remainder of this section (and most books on the subject) will assume that XSLT is used as the transformation system. 7.5.1. JAXP Data SourcesThe DOM example earlier in this chapter briefly introduced the transformerFactory class. transformerFactory works just like the SAX and DOM parser factories, but returns a transformer object instead of a parser. The default behavior of a transformer is to pass the input XML through without modification, which is what will occur if no transformation stylesheet is given. Input and output from a transformer are handled via the javax.xml.transform.Source and javax.xml.transform.Result interfaces. Each has three implementing classes, one each for DOM, SAX, and streams (DOMSource, SAXSource, StreamSource, DOMResult, SAXResult, and StreamResult). Note that the JAXP processors will not necessarily support all six. We handled the output in the DOM example by creating a DOMSource and outputting to a StreamResult that targeted System.out (in this example, the document variable contains a DOM Document object): TransformerFactory tf = TransformerFactory.newInstance( ); Transformer output = tf.newTransformer( ); output.transform(new DOMSource(document), new StreamResult(System.out)); The Source objects can also specify the source of the stylesheet used for the conversion. If we want to use a stylesheet located in /home/will/orderdisplay.xsl: TransformerFactory tf = TransformerFactory.newInstance( ); Transformer output = tf.newTransformer( new StreamSource("file://home/will/orderdisplay.xsl")); output.transform(new DOMSource(document), new StreamResult(System.out)); The different source and result types can streamline processing. Rather than load an XML file, transform it, write it to disk, reload it, and parse it with a SAX ContentHandler, the transformation process can feed its results directly into a ContentHandler that can deal with the transformation results. Example 7-4 shows transformation of a document and its immediate processing by a SAX ContentHandler. We'll use the OrderHandler program from earlier in the chapter as the content handler, and we'll use the same orders.xml file. To keep things simple, we'll do a one-to-one transformation, rather than use an XSLT file to actually alter the structure of the orders.xml file. Example 7-4. Transforming a document into a SAXResultimport javax.xml.transform.*; import javax.xml.transform.sax.*; import javax.xml.transform.stream.*; import org.xml.sax.*; import org.xml.sax.helpers.*; public class SAXTransformTarget { public static void main(String[] args) { try { StreamSource ss = new StreamSource("orders.xml"); SAXResult sr = new SAXResult(new OrderHandler( )); TransformerFactory tf = TransformerFactory.newInstance( ); Transformer t = tf.newTransformer( ); t.transform(ss, sr); } catch (TransformerConfigurationException e) { e.printStackTrace( ); } catch (TransformerException e) { e.printStackTrace( ); } } The output from this program should be identical to the output from Example 7-1; we've simply replaced the XMLReader with a transformation stream. 7.5.1.1. Determining data source supportThe transformerFactory class includes a method named getFeature( ), which takes a String and returns TRue if the feature identified by the String is supported by the processor. Each of the Source and Result implementations includes a String constant named FEATURE. So to determine whether the XSL processor supports DOM source: TransformerFactory tf = TransformerFactory.newInstance( ); boolean supportsDOMSource = tf.getFeature(DOMSource.FEATURE); 7.5.1.2. Custom URI resolutionWhen processing XSL files, it is sometimes necessary to resolve relative URIs. Ordinarily, the parser will do the best possible job, but sometimes it is necessary to override this behavior (for instance, in a web content management system in which the document tree apparent to the content creator and client might not match the system structure or when XML output from a servlet is being transformed within the servlet). In these cases, the setURIResolver(URIResolver) method of transformer allows you to specify resolution behavior by implementing the URIResolver interface. The resolve( ) method of URIResolver must return a Source or null: class XSLResolver implements URIResolver { public Source resolve(String href, String base) throws TransformerException { // Check for a null Base URI, and provider it if so if((base == null) || (base.equals("/servlet/"))) base = "http://www.oreilly.com/catalog/jentnut3/"; if(href == null) return null; return new StreamSource(base + href); } } Finally, JAXP supports XSLT transformations that can convert between DOM, SAX, and streams; transform an XML document based on an XSL stylesheet; or both. |