Sei sulla pagina 1di 13

XSL gives your XML some style

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


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

XSL gives your XML some style


Use XSL and servlets to style your XML data
xmlns:java="http://xml.apache.org/xslt/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.

Command Result
<xsl:value-of select="employee/name"/> Joe Shmo
<xsl:value-of select="employee/@id"/> 03432
Accessing elements versus attributes
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 gives your XML some style


Use XSL and servlets to style your XML data
<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="http://xml.apache.org/xslt/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

XSL gives your XML some style


Use XSL and servlets to style your XML data
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.

XSL gives your XML some style


Use XSL and servlets to style your 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:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:java="http://xml.apache.org/xslt/java"
exclude-result-prefixes="java"
version="1.0">
<xsl:output method="html" indent="yes"/>
<xsl:template match="/orders">
<html>
<h1>Today's Invoices</h1>
<body>
<table border="on" cellspacing="1" cellpadding="5">
<thead>
<th>Invoice Number</th>
<th>Customer Name</th>
<th>Total Purchase</th>
</thead>
<tbody>
<xsl:for-each select="invoice">
<tr>
<td>
<xsl:element name="a">
<xsl:attribute
name="href"><xsl:text>xsl?stylesheet=detail&invoiceNum=</xsl:text><xsl:value-
of
select="@number"/></xsl:attribute>
<xsl:value-of select="@number"/>
</xsl:element>
</td>
<td><xsl:value-of select="name"/></td>
<td><xsl:value-of
select="java:SumInvoice.summ(item)"/></td>
</tr>
</xsl:for-each>
</tbody>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>

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:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:java="http://xml.apache.org/xslt/java"
exclude-result-prefixes="java"
version="1.0">
<xsl:param name="invoiceNum" select="string('none')"/>
<xsl:output method="html" indent="yes"/>
<xsl:template match="/">
<html>
<body>
<table border="on" cellspacing="1" cellpadding="5">
<xsl:for-each select="orders/invoice[@number=$invoiceNum]">
<tr>
<td>
<table border="on" cellspacing="1" cellpadding="5">
<tr><td><b>Invoice
Number:</b><xsl:value-of select="@number"/></td><td
colspan="3"><xsl:value-of select="name"/></td></tr>
<tr>
<td colspan="4">
<xsl:apply-templates select="address"/>
</td>
</tr>
<tr><td
colspan="4"><b>Items</b></td></tr>

<tr><td>Part</td><td>Quantity</td><td>Cost</td><td>Subtotal</td></tr>
<xsl:for-each select="item">
<tr>
<td><xsl:value-of select="@part"/></td>
<td><xsl:value-of
select="@quantity"/></td>
<td><xsl:value-of select="@cost"/></td>
<td><xsl:value-of select="@quantity *
@cost"/></td>
</tr>
<xsl:if test="last()">
</xsl:if>
</xsl:for-each>
<tr>

<td><b><xsl:text>Total</xsl:text></b></td>
<td><xsl:value-of
select="sum(item/@quantity)"/></td>
<td><xsl:text></xsl:text></td>
<td>
<xsl:value-of select="java:SumInvoice.summ(item)"/>
</td>
</tr>
</table>
</td>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
<xsl:template match="address">
<table>
<tr><td><i>
<xsl:choose>
<xsl:when test="@type='shipping'">
<xsl:text>Shipping Address</xsl:text>
</xsl:when>
<xsl:when test="@type='billing'">
<xsl:text>Billing Address</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text>Other Address</xsl:text>
</xsl:otherwise>
</xsl:choose></i>
</td></tr>
<tr><td colspan="4"><xsl:value-of
select="street"/></td></tr>
<tr><td colspan="4"><xsl:value-of
select="city"/><xsl:text> </xsl:text><xsl:value-of
select="state"/>, <xsl:value-of
select="zip"/></td></tr>
</table>
</xsl:template>
</xsl:stylesheet>

XSL gives your XML some style


Use XSL and servlets to style your XML data

Figure 2. Stylesheet 3: Output from the Detail view (48 KB)

Wrap it up
The servlet will extend the HTTPServlet Java class and employ the doPost() and doGet()
methods.
The XSLInputSource gives us the ability to just hand the XSLProcessor a java.io.Reader
object -- perfect since our XML and XSL will reside in flat files. If we had these in any other
place, we could just use a StringReader. The XSLInputSource will also accept a
org.w3c.dom.Node.
The XSLTResultTarget writes out to an OutputStream -- convenient, since we can get an
OutputStream from our HTTPResponse.
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import java.util.*;
import com.lotus.xsl.XSLTInputSource;
import com.lotus.xsl.XSLTResultTarget;
import com.lotus.xsl.XSLProcessor;
import org.xml.sax.SAXException;
public class XSLServlet extends HttpServlet {
public void init(ServletConfig config) throws ServletException {
super.init(config);
}
//By redirecting our doGets to the doPost method we can have a stylesheet
//executed from a link
//ex. <a
href="xsl?stylesheet=detail&invoiceNum=00002">00002
public void doGet(HttpServletRequest request, HttpServletResponse
response) throws ServletException, IOException {
doPost(request, response);
}
//Process the HTTP Post request
public void doPost(HttpServletRequest request, HttpServletResponse
response) throws ServletException, IOException {
PrintWriter out = new PrintWriter (response.getOutputStream());
//Get the stylesheet selected by the user
String stylesheet = request.getParameter("stylesheet");
//Get the invoice number selected by the user, this is used only for
certain
//stylesheets. If it is not present, the stylesheet won't use it.
String invoiceNum = request.getParameter("invoiceNum");
//Prepare FileReaders for the stylesheet and XML
FileReader xslReader = new
FileReader(".\\javaworld\\xsl\\"+stylesheet+".xsl");
FileReader xmlReader = new
FileReader(".\\javaworld\\xml\\"+"invoice.xml");
response.setContentType("text/html");
try
{
//Create an instance of an XSLProcessor
XSLProcessor proc = new XSLProcessor();
//Set the invoice number parameter
proc.setStylesheetParam("invoiceNum", invoiceNum);
//Process the stylesheet, all the output will go straight
// out to the browser.
proc.process(new XSLTInputSource(xmlReader),new
XSLTInputSource(xslReader),new XSLTResultTarget(out));
}
catch (SAXException saxE)
{
out.write(saxE.getMessage());
saxE.printStackTrace(out);
}
out.close();
}
//Get Servlet information
public String getServletInfo() {
return "XSLServlet Information";
}
}

And that's all there is to it!


Conclusion
You have seen how to extract XML as HTML, using XSL. But as I mentioned in the beginning
of the article, XSL can be used to transform XML from one doc type to another -- XML to
HTML, HTML to XML, HTML to HTML, XML to DSML (Directory Service Markup
Language), and so on. XSL's most useful feature: it doesn't have to be compiled. The way the
servlet example works, we could add stylesheets to our stylesheet folder, then add the stylesheet
to our list of stylesheets in our HTML.

Another handy XSL use: take a java.sql.Result set and turn it into a simple XML document
in which the name of the query is the root node and each row in the result table is an element in
our XML document. Then the subelements of that element are the columns and values from that
row. Taking that plain, well-formed XML document, we can simply apply XSL to it, creating an
XML document that conforms to a desired schema. That would be nice for EDI (Electronic Data
Interface) or anything else where you need to extract data from a relational database such as
XML.
A word on performance: the XSLProcessor runs very fast; it completes transformations in
subsecond time. Performance degrades as the stylesheet does more processing or more complex
processing. The input XML has to get pretty big -- over 100 KB -- before you will notice a
significant decrease in performance.
In conclusion, XSL provides an easy way to change XML to HTML. But it can do much more
than that. The extension mechanism gives added functionality to the basics of XSL. Using
servlets to process your XSL will help you keep your content and presentation separate.

Potrebbero piacerti anche