Wow, you've come so far. A year ago you didn't know what XML was, but now it's everywhere. You're storing XML in databases and using it in middleware, and now you want to present that data to a browser. That's where XSL can help. XSL can help you turn your XML into HTML. Moreover, servlets provide a powerful medium to perform those translations as they reside on the server and have access to all the features of server-side Java.
In this article, we'll cover the basics of XSL and XSL processors. If you don't know much about XML, you may want to first read Mark Johnson's excellent XML article, "Programming XML in Java, Part 1."
Our example will use a servlet to turn well-formed XML into HTML. If you need to learn more about servlets, please refer to Sun's servlets tutorial (see Resources).
XML and XSL
The process of transforming and formatting information into a rendered result is called styling. Two recommendations from the W3C come together to make styling possible: XSL Transformations (XSLT), which allows for a reorganization of information, and XSL, which specifies the formatting of the information for rendering.
With those two technologies, when you put your XML and XSL stylesheet into an XSL processor, you don't just get a prettied up version of your XML. You get a result tree that can be expanded, modified, and rearranged.
An XSL processor takes a stylesheet consisting of a set of XSL commands and transforms it, using an input XML document. Let's take a look at a simple example.
Below we see a small piece of XML, describing an employee. It includes his name and title. Let's assume that we would like to present that in HTML.
<employee id="03432"> <name>Joe Shmo</name> <title>Manager</title> </employee>
If we wanted our HTML to look like this:
<html> <body> <p><b>Joe Shmo</b>: Manager</p> </body> </html>
Then we could use a stylesheet, such as the one below, to generate the HTML above. The stylesheet could reside in a file or database entry:
<xsl:stylesheet xmlns:xsl=""> <xsl:template match="/"> <html> <body> <p> <b> <xsl:value-of select="employee/name"/> </b> <xsl:text>: </xsl:text> <xsl:value-of select="employee/title"/> </p> </body> </html> </xsl:template> </xsl:stylesheet>
Common XSL stylesheet commands
The stylesheet declaration consists of a version and namespace. The namespace declares the prefix for the tags that will be used in the stylesheet and where the definition of those tags are located:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> . . . </xsl:stylesheet>
If there are any extensions referenced, the namespace must be specified. For example, if you were going to use Java, you would specify this namespace:
When selecting in a stylesheet, a pattern is used to denote which element or attribute we want to access. The syntax is simple: specify the node you want, using
/ to separate the elements.
Notice that in the sample XML code above we matched our template on
/, which is the root node. We could have, however, matched on the
employee node. Then a select statement could just refer to the
name node instead of
For example, if we had the following XML:
<employee id="03432"> <name>Joe Shmo</name> <title>Manager</title> </employee>
Attributes can also be selected. The employee id could be accessed by saying
employee/@id. Groups of nodes can be accessed by using
employee/*. A specific employee could be located using
Pattern matching allows us to select specific values out of our XML document. The
<xsl:value-of select... command gives us the ability to select a value for our resulting XML document, as seen in the table below.
Templates provide a way to match nodes in an XML document and perform operations on them. The syntax for a template is:
<xsl:template match="nodename"> . . . </xsl:template>
The template is matched on a node name, then all the stylesheet commands in that template are applied. We can call templates in our stylesheet by using the
An example using our employee XML above would be:
<xsl:template match="name" <xsl:value-of select="."/> </xsl:template>
We can call this template anywhere there is a name node to be referenced, using this:
There are a few structures available for doing ifs and loops. Let's take a look at the syntax.
choose command provides a structure to test different situations.
<xsl:choose> <xsl:when test="test situation"> stylesheet commands </xsl:when> <xsl:otherwise> stylesheet commands </xsl:otherwise> </xsl:choose>
The first successful test will result in that block's stylesheet commands executing. If all the tests fail, the
otherwise block is executed. You may have as many
when blocks as you want. The
otherwise block must always be present; if you don't want to do anything in your
otherwise block, just put:
if command provides only a single test and doesn't have any kind of else structure available. If you need to have an else, use the
<xls:if test="test situation"> ... </xsl:if>
Loops (for-each command)
Unlike most languages with for and while structures, XSL offers only the
for-each command. As such, you can loop on a set of nodes or you can select the nodes you want to loop on, using a pattern match:
<xsl:for-each select="select statement"> ... </xsl:for-each>
For example, if you had more than one employee in your XML document, and you wanted to loop through all the managers, you could use a statement such as this:
<xsl:for-each select="employee[title='Manager']"> ... </xsl:for-each>
variable command provides a way to set a variable and access it later. The extension mechanism uses variables to store the values retrieved from extensions:
<xsl:variable name="count">assign value to count here</xsl:variable>
The variable count can be accessed by using
$count later in the stylesheet:
You can pass parameters to your stylesheet, using the
param tag. You can also specify a default value in a select statement. The default is a string, so it must be in single quotes:
<xsl:param name="param1" select="'default value'"/>
You can set the parameters you are passing to your stylesheet on your
Extensions add functionality to a stylesheet. XSL comes with some basic functions:
sum()-- Sum the values in designated nodes
count()-- Count the nodes
position()-- Returns the position of the current node in a loop
last()-- Test whether this is the last node; this function returns a boolean value
In order to call extensions in Java, the
java namespace must be specified in your stylesheet declaration:
Any calls to Java extensions would be prefaced with
java:. (Note: You don't have to call your namespace
java; you can call it whatever you want.)
You can do three things with Java extensions: create instances of classes, call methods on those classes, or just call static methods on classes. Table 2 shows syntax that can be used to reference Java objects.
Instantiate a class:
prefix:class.new (args) Example:
variable myVector select"java:java.util.Vector.new()"
Call a static method:
prefix:class.methodName (args) Example:
variable myString select="java:java.lang.String.valueOf(@quantity))"
Call a method on an object:
prefix:methodName (object, args) Example:
variable myAdd select="java:addElement($myVector, string(@id))" Table 2. Three ways to use Java objects
(For more on XSL, see Elliotte Harold's The XML Bible in Resources.)
The XSL Processor API
For our example, we will use Lotus' implementation of Apache's XSL processor Xalan (see Resources). We'll use the following classes in our servlet example:
com.lotus.xsl.XSLProcessor is the processor that implements the functionality defined in
org.apache.xalan.xslt.XSLTProcessor. The default constructor can be used and processing can take place using the
process() method, as seen below:
void process(XSLTInputSource inputSource, XSLTInputSource stylesheetSource, XSLTResultTarget outputTarget)
process() method transforms the source tree to the output in the given result tree target.
void reset() method, to be used after
process(), resets the processor to its original state.
process() method is overloaded 18 times. Each signature provides a different way to process your XML and XSL. Some return an
org.w3c.dom.Document object. I have found that the above
process() method is the handiest; the documentation recommends its use because of the
XSLTInputSource (used to read in XML or XSL) and
XSLTResultTarget (used to write out the results) classes, which we examine next in turn.
XSLInputSource class can be created by using any of the following constructors:
XSLTInputSource(): Zero-argument default constructor
XSLTInputSource(org.xml.sax.InputSource isource): Creates a new XSLTInputSource source from a SAX input source
XSLTInputSource(java.io.InputStream byteStream): Creates a new input source with a byte stream
XSLTInputSource(org.w3c.dom.Node node): Creates a new input source with a DOM node
XSLTInputSource(java.io.Reader characterStream): Creates a new input source with a character stream
XSLTInputSource(java.lang.String systemId): Creates a new input source with a system identifier
XSLResultTarget class can be created by using any of the following constructors:
XSLTResultTarget(): Zero-argument default constructor
XSLTResultTarget(org.xml.sax.DocumentHandler handler): Creates a new output target with a SAX Document handler, which will handle result events
XSLTResultTarget(org.w3c.dom.Node n): Creates a new output target with a character stream
XSLTResultTarget(java.io.OutputStream byteStream): Creates a new output target with a byte stream
XSLTResultTarget(java.lang.String fileName): Creates a new output target with a file name
XSLTResultTarget(java.io.Writer characterStream): Creates a new output target with a character stream
The API also includes classes that enable you to listen for events -- problems or document events, for example -- during processing.
Put it all together
Now that we have a basic overview of the XSL API and XSL stylesheets, let's look at a problem that those technologies could help us solve.
In this example, we've got a server-side process that uses XML to store transaction data. There is a business need for the ability to analyze the day's transactions. The solution should be as unobtrusive as possible, leaving the company's current process and data storage procedures unchanged.
Here are some of the possible solutions using XSL:
- Create a periodically running program on the server that will use XSL to transform XML into HTML. The resulting HTML files will be stored on a file server where they can be accessed by a rowser.
- If you have a controlled set of users, make sure they have browsers that support XSL. In that case, your XML will include a header denoting which XSL document to use, allowing the browser to perform the translation for you.
- Use XSL in a server-side medium such as servlets. Pull the XML from wherever it may be in a file, a database, or a message queue, then style the XML using XSL.
We'll use approach three, but the first and second approaches are both valid. Approach one would solve any performance problems, but it doesn't provide the dynamic HTML creation that will make our application special. Approach two will be a real possibility soon -- Internet Explorer 5 can do XSL translations, and Netscape has announced that its next release will, too. However, your users won't all have the newest browsers, and we still have to do server-side processing to pull the XML data.
Moving on, let's look at the XML schema for our transactions: