XML's power lies in its ability to represent data. However, to do something useful with the XML data, especially if it is document-centric XML, it would typically need to be translated into another format. The technology that enables this translation is the eXtensible Stylesheet Language (XSL). XSL is a language for expressing style sheets. A style sheet is an XML document that describes how to display an XML document of a particular type. XSL is a W3C specification and is broken down into three complementary technologies:
XSL Transformations (XSLT). Defines a language for expressing transformation rules from one class of XML document to another. XSLT specifications can be found at www.w3.org/TR/xslt.
XML Path Language (XPath). An expression language used by XSLT to access or refer to parts of an XML document. XPath specifications can be found at www.w3.org/TR/xpath.
XSL Formatting Objects (XSL-FO). A language for defining formatting, such as fonts and page layout. These are defined as a part of the XSL specification at www.w3.org/TR/xsl.
An XSLT processor, analogous to an XML parser, is an application that applies an XSL style sheet (which itself is an XML document and conforms to the XSL schema) to XML data input. Instead of modifying the original XML data, XSLT produces a result—typically HTML or XML and, in some rare cases, binary. Essentially, XSL uses XSLT to transform an XML source tree into an XML result tree, as Figure 9.5 shows.
These trees are logical structures that consists of nodes that may be produced by XML. They may be implemented as object models (e.g., DOM), a series of well-balanced parse events (e.g., callbacks received by the SAX ContentHandler), a series of requests (the result of which can describe a tree), or a stream of marked-up characters.
Consider the example where you want to display part of the flutebank.com administrator information from the XML in Listing 9.1. Listing 9.5 shows the XSL a processor would take. Figure 9.6 shows the resulting output from the transformation, as rendered in a browser.
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:n1="http://www.flutebank.com/schema" xmlns:contacts="http://www.flutebank.com/ schema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <xsl:template match="/"> <html> <body> <h4>Flutebank.com : Support personal contact information</h4> <table bgcolor="#AAB0AC" border="1" cellpadding="0" cellspacing="0" width="50%"> <tr> <td> <h4>Name</h4> </td> <td> <xsl:for-each select="n1:flutebank"> <xsl:for-each select="n1:administrator"> <xsl:for-each select="n1:firstname"> <xsl:apply-templates /> </xsl:for-each> </xsl:for-each> </xsl:for-each> <xsl:for-each select="n1:flutebank"> <xsl:for-each select="n1:administrator"> <xsl:for-each select="n1:lastname"> <xsl:apply-templates /> </xsl:for-each> </xsl:for-each> </xsl:for-each> </td> </tr> <tr> <td> <h4>Phone:</h4> </td> <td> <xsl:for-each select="n1:flutebank"> <xsl:for-each select="n1:administrator"> <xsl:for-each select="n1:telephone"> <xsl:for-each select="n1:desk"> <xsl:apply-templates /> </xsl:for-each> </xsl:for-each> </xsl:for-each> </xsl:for-each> </td> </tr> <<tr> <td> <h4>Email:</h4> </td> <td> <xsl:for-each select="n1:flutebank"> <xsl:for-each select="n1:administrator"> <xsl:for-each select="n1:email"> <xsl:for-each select="n1:work"> <xsl:apply-templates /> </xsl:for-each> </xsl:for-each> </xsl:for-each> </xsl:for-each> </td> </tr> </table> </body> </html> </xsl:template> <xsl:template match="n1:administrator"> <xsl:apply-templates /> </xsl:template> <xsl:template match="n1:cellular"> <xsl:apply-templates /> </xsl:template> </xsl:stylesheet>
As with XML, this chapter is not intended to be a lesson on XSL or XPath syntax; numerous good sources are available for that. Vendor tools are also available, such as XMLSpy (www.xmlspy.com), that can generate XSL code based on the input XML and desired output, with minimal knowledge of these languages on the part of the user.
So, why is XSLT important? A typical enterprise application that delivers content to users would use data from multiple EIS resources, apply some business rules and processing to the data, and serve the clients. As Figure 9.7a shows, the clients could be other applications, users, or devices, all using the same logical data. To prevent rewriting the application and the presentation tier for every interface, an XSL-based processor that transcodes same document-centric XML into different formats (XML to WML, HTML, XML, XHTML, etc.) using XSL offers a cleaner content delivery mechanism but the same logical data set. The logical tiering for this approach is shown in Figure 9.7b.
JAXP architecture accommodates XSLT by providing the same abstraction and pluggability as it does for SAX and DOM (see Figure 9.1a).
As Figure 9.8 shows, JAXP is used to instantiate a TransformerFactory, which, in turn, is used to obtain a reference to a Transfomer specific to a style sheet. A Transformer takes a Source and transforms it into a Result. The source can be of any type: a DOMSource,a SAXSource (or the content parsed by a SAX parser), or a StreamSource.
The JAXP part that handles transformations is also known as the Transformation API for XML (TrAX) and is contained in the following packages:
javax.xml.transform. Defines the core interfaces for transformation.
javax.xml.transform.sax. Defines the SAX-relevant interfaces for transformation.
javax.xml.transform.dom. Defines the DOM-relevant interfaces for transformation.
javax.xml.transform.stream. Defines the Stream-relevant interfaces for transformation.
To put the earlier mention about a source tree and a result tree into perspective, it is interesting to note that if the reference implementation, Xalan, is supplied with a DOMSource, it will use this as its internal tree representation. If the input is a SAXSource or StreamSource, it uses its own internal tree representation as the source tree, called an STree. This is more efficient than a general-purpose DOM, because it contains additional sorting optimizations.
Tables 9.7 through 9.10 show the relevant interfaces in the above packages.
The code to do the transformation would look something like the following:
String xml = "saxexaple2.xml"; String xsl = "fluteadmin.xsl"; // instantiate the factory TransformerFactory factory = TransformerFactory.newInstance(); // obtain a transformer from the factory for the XSL Transformer transformer = factory.newTransformer (new StreamSource(new File(xsl))); // use the transformer to transform the source to a result transformer.transform(new StreamSource(new File(xml)), new StreamResult(System.out));
Another example would be to transform a DOM into another DOM in memory, using the XSL that is also in memory as a DOM (remember, an XSL document is also an XML document).
Source |
Defines the top level interface to wrap other specific sources. |
Result |
Defines the top level interface to wrap other specific results. |
Templates |
A template represents a set of XSL instructions in memory that need to be applied repeatedly to different XML data structures. |
Transformer |
The processor that applies the transformation. |
TransformerFactory |
A factory used to obtain a reference to the underlying Transformer. |
URIResolver |
Like schemas, a style sheet can have references to other style sheets, using an xsl:include or xsl:import statement. This is used to resolve such references and process them. |
DOMSource |
An interface to represent a DOM tree (an org.w3c.dom.Node) as the source of the XML data to be transformed |
DOMResult |
An interface to represent a DOM tree (an org.w3c.dom.Node) as the result of the transformation |
String xml = "saxexaple2.xml"; String xsl = "fluteadmin.xsl"; // create a DOM from the XSL DocumentBuilderFactory domfactory = DocumentBuilderFactory.newInstance(); domfactory.setNamespaceAware(true); DocumentBuilder parser = domfactory.newDocumentBuilder(); Document xsldom = parser.parse(xsl); // create a DOM from the source XML Document xmldom = parser.parse(xml); // create the transfomer and transform the xml TransformerFactory factory = TransformerFactory.newInstance(); Transformer transformer = factory.newTransformer(new DOMSource(xsldom)); DOMResult result= new DOMResult(); transformer.transform(new DOMSource(xmldom),result);
SAXSource |
An interface to represent a SAX as the source of the XML data to be transformed. This wraps an org.xml.sax.InputSource and can futher be piped on a java.io.InputStream, java.io.Reader, or URI. (Typically, a string representing a java.net.URL.) |
SAXResult |
An interface to wrap a SAX ContentHandler invoked sequentially as a result of applying the transformation to the XML |
TemplatesHander |
A SAX ContentHandler that can be used to generate a Templates object from SAX source events |
TransformerHandler |
A SAX ContentHandler that can be used to generate a Result object from SAX source events |
SAXTransformerFactory |
A special kind of TransformerFactory that can be used to configure the SAX parser processing the XSL input |
StreamSource |
An interface to act as a pipe on different streams, as sources for the transformation (File, InputStream, Reader, or URL) |
StreamResult |
An interface to act as a pipe on different streams, as results for the transformation (File, OutputStream, Write, or URL) |
Another example would be to use an empty transfomer not associated with a particular style sheet to transform the DOM—for example, from the code above into another form:
// As a convenience , print out the in memory DOM to // the System.out using a null transform TransformerFactory factory = TransformerFactory.newInstance(); Transformer nulltransformer = factory.newTransformer(); nulltransformer.transform(new DOMSource(result.getNode()), new StreamResult(System.out));
Another example would be to use the transformer to obtain the associated style sheet from an XML document—for example, if the XML has a processing instruction such as:
<?xml-stylesheet type="text/xsl" href="fluteadmin.xsl"?>: String xml = "pitransform.xml"; TransformerFactory factory = TransformerFactory.newInstance(); Source xsl = factory.getAssociatedStylesheet ( new StreamSource(xml),null, null, null); Transformer transformer = factory.newTransformer(xsl); transformer.transform(new StreamSource(xml),new StreamResult(System.out));
A common use for transformation involves applying the same style sheet to different or dynamically generated XML data, to produce dynamic output. For example, users request a service from a flutebank.com account service, resulting in the generation of user-specific stock portfolio XML data that needs to be transformed for rendering. In such scenarios, a Templates object should be used instead, to obtain the Transformer. A Templates object is immutable, thread safe, and guaranteed to work across multiple threads. Also, as we shall see with XSLTc, underlying implementations may provide further optimizations to deal with repeated transformations.
String xml_1 = "pitransform.xml"; String xml_2 = "saxexample2.xml"; String xsl = "fluteadmin.xsl"; // instantiate the factory TransformerFactory factory = TransformerFactory.newInstance(); // obtain a template from the factory Templates template = factory.newTemplates(new StreamSource(xsl)); Transformer transformer = template.newTransformer(); // use the transformer to transform the source to a result transformer.transform(new StreamSource(new File(xml_1)), new StreamResult(System.out)); transformer.transform(new StreamSource(new File(xml_2)), new StreamResult(System.out));