XSL gives your XML some style | InfoWorld

Use XSL and servlets to style your XML data

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>
    

<b>Joe Shmo</b>: Manager

</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>
        

<b> <xsl:value-of select="employee/name"/> </b> <xsl:text>: </xsl:text> <xsl:value-of select="employee/title"/>

</body> </html> </xsl:template> </xsl:stylesheet>

Common XSL stylesheet commands

Stylesheets are defined by a set of XSL commands. They make up valid XML documents. Stylesheets use pattern matching to locate elements and attributes. There are also expressions that can be used to call extensions — either Java objects or JavaScript. Let’s look at some XSL commands.

Stylesheet declaration

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=" 
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:

xmlns:java="

Pattern matching

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 employee/name.

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 employee/@id='03432'.

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.

Accessing elements versus attributes
Command Result
<xsl:value-of select="employee/name"/> Joe Shmo
<xsl:value-of select="employee/@id"/> 03432

Templates

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 apply-templates command:

<xsl:apply-templates select="nodename"/>

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:

<xsl:apply-templates select="name"/>

Logical commands

There are a few structures available for doing ifs and loops. Let’s take a look at the syntax.

Choose command

The 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:

<xsl:otherwise/>

If command

The 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 choose command.

<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>

Variables

The 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:

<xsl:value-of select="$count"/>

Parameters

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 XSLProcessor object:

processor.setStylesheetParam("param1", processor.createXString("value"));

Extensions

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

If you want additional functionality, you need to use extensions. Extensions can be called anywhere a value can be selected. Extensions to a stylesheet can be written in Java or JavaScript, among other languages. We’ll concentrate on Java extensions in this article.

In order to call extensions in Java, the java namespace must be specified in your stylesheet declaration:

xmlns:java="

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:

Class com.lotus.xsl.XSLProcessor

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)

The process() method transforms the source tree to the output in the given result tree target.

The void reset() method, to be used after process(), resets the processor to its original state.

The 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.

Class com.lotus.xsl.XSLInputSource

The 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

Class com.lotus.xsl.XSLResultTarget

The 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:

  1. 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.
  2. 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.
  3. 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:

<!ELEMENT orders (invoice)+>
<!ELEMENT invoice (item+,name,address+)>
<!ELEMENT item EMPTY>
<!ELEMENT name (#PCDATA)>
<!ELEMENT address (street, city, state, zip)>
<!ELEMENT street (#PCDATA)>
<!ELEMENT city (#PCDATA)>
<!ELEMENT state (#PCDATA)>
<!ELEMENT zip (#PCDATA)>
<!ATTLIST invoice
  number        CDATA #REQUIRED
  orderDate     CDATA #REQUIRED
  shipDate      CDATA #REQUIRED>
<!ATTLIST item
  part          CDATA #REQUIRED
  quantity      CDATA #REQUIRED
  cost          CDATA #REQUIRED>
<!ATTLIST address
  type          (shipping|billing|other) #REQUIRED>

Note: XSL doesn’t require a DTD to do its processing — a good feature. Therefore, if minor changes occur in the schema, the same stylesheets can still be used.

In our example, four stylesheets will be created, each representing a different view:

  1. Transaction view: A list of each day’s transactions, including invoice number, customer name, and invoice total.
  2. Products sold view: A list of all the products sold for each day, including the quantity.
  3. Detail view: A drill-down from the Transaction and Products sold views. When the user clicks the invoice number, the details for the transaction display.
  4. XML view: A display of the XML we are processing against.

Those examples will lack spinning logos, thus demonstrating the power of XSL without cluttering the output with graphics.

The HTML that calls the servlet will use frames. The left frame will have a list of the stylesheets available. When a stylesheet is selected, a request will be sent to the servlet. Then the servlet will open the XML and the stylesheet, and use the XSLProcessor to do the translation. The translation will be in the form of HTML, which will be pushed out to the browser.

Let’s go over the Transaction view, which shows the list of invoices, then the Detail view, which shows the details of the invoice.

The Transaction view

Here’s the Transaction view — a list of the day’s invoices:


Figure 1 shows the results.

Figure 1. Stylesheet 1: Output from the Transaction view (40 KB)

The Detail view

Now we turn our attention to the Detail view:




Source: www.infoworld.com