XSLT Tutorial

Extensible Stylesheet Language Transformations (XSLT) is an XML based language that provides a method of transforming an XML document into another XML document. The intention is to convert it into a human readable document. In this example I’m converting an XML data file into an XHTML document.

To use XSLT we need two files, an XML document and an XSL transform. I’ll be using Movies.xml as my data file for all of the examples. There will be different transform fies for each example, so I’ll provide the correct links as needed.

When using only XSLT you need to include a tag in the XML document that identifies the transform file to use. This is done like this:
<?xml-stylesheet type="text/xsl" href="MovieList.xsl"?>
The href value gives the exact file we’re going to use for the transform. So when the browser attempts to load this XML document it will also load the transform file and attempt to make the conversion.

Lets take a look at a basic XSL file:
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="/">
    <html>
      <head>
        <title>Movie List</title>
      </head>
      <body>
        <h2>Movie Collection</h2>
        <table border="1">
          <tr>
            <th>Title</th>
            <th>Genre</th>
            <th>Year</th>
          </tr>
        </table>
      </body>
    </html>
  </xsl:template>
</xsl:stylesheet>

It looks a lot like regular XHTML, aside from the new header tags it is just plain XHTML. In most cases we’ll be using XSLT to insert XML data values into an XHTML block; so the XML provides the data, XHTML provides the formatting, and the XSL explains how to mix them together.

Let me explain the tags included that aren’t regular XHTML.
<?xml version="1.0" encoding="ISO-8859-1"?>
Since XSLT is an XML based language we need to include a header to define it as XML.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
This is the line that really defines this as an XSL transform file. Don’t forget its closing tag at the very end.
<xsl:template match="/">
This line begins our XSL processing. Each XHTML code block will be contained inside a xsl:template tag. The purpose of the tag is to define the area of the XML data that will be used in this block. XSLT uses relative paths similar to file systems to navigate the XML data. This XML path is added to the match attribute to identify the tag this particular XSL template will work from. Using just a forward slash means that we’d like to use the entire XML file.

When we combine the XML data file with the the XSL transform file we get this result, which is rather disappointing; it doesn’t include anything from the XML data file. Thats actually ok, we haven’t told it to use any of the XML data yet. We’ll need to add in some xsl:value-of tags to get the data in there.

Those tags look like this:
<xsl:value-of select="/List/Movie/Title" />
The select attribute states which value to retrieve. Following the path we’re starting from the root then look for a tag named List, then going into a sub-tag named Movie, and retrieving the value of a tag named Title. It will insert this value directly into the XHTML code.

I’ve added a few of them into this XSL file. Combining it with the XML data gives us this result. Which is a little better, we’re seeing something from the XML data now. The problem we have now is that all of our tags share the same name, so our path can’t identify them and we only get the first one.

To get around that problem we need to use a loop. XSLT has a xsl:for-each tag that will loop through each tag that matches your path and allow you to take the action on each one. Here’s a loop that will let us get values from all of the Movie tags in the XML data file:
<xsl:for-each select="List/Movie">
  <tr>
    <td><xsl:value-of select="Title" /></td>
    <td><xsl:value-of select="Genre" /></td>
    <td><xsl:value-of select="Year" /></td>
  </tr>
</xsl:for-each>

The select attribute of the xsl:for-each tag lets us set the path that we’d like to use, it will then find all matches for that path and execute the code inside the tag for each one. I’m taking advantage of the relative paths in this tag. Since the parent of the xsl:for-each tag was using the path /, set in the xsl:template tag, we can remove that part of the path and just use the rest. I’m doing the same thing with the xsl:value-of paths; the xsl:for-each set the current path to /List/Movie, so all I need to do is give any parts of the path that follow.

I’ve added the loop into this XSL file. Combining it with the XML data gives us this result. Now its starting to become useful. The XSLT is putting all of the XML data into the final page, and its a bit easier to read than the original XML data.

XSL will also let us sort the data inside of the loop. There is an xsl:sort tag that lets us specify a value to sort by. Then when the for each loop goes through the data will be put in order based on that value. This tag goes directly after the xsl:for-each tag, like this:
<xsl:for-each select="List/Movie">
<xsl:sort select="Title" />
  <tr>
    <td><xsl:value-of select="Title" /></td>
    <td><xsl:value-of select="Genre" /></td>
    <td><xsl:value-of select="Year" /></td>
  </tr>
</xsl:for-each>

The select value of the xsl:sort tag states the value we’d like to sort on. In this case we’re sorting things alphabetically by title. Here is the full XSL file and the sorted results.

What happens if we don’t want everything to be the same? Perhaps we need to treat some values differently. XSLT will let us do that in two ways. There are tags we can use to test the data and handle values in different ways.

Lets start with the basic if-then conditional, which is handled by the xsl:if tag. Lets say that we only want to have movies with a genre of Sci-Fi be shown. Our loop would include an xsl:if tag like this:
<xsl:for-each select="List/Movie">
<xsl:sort select="Title" />
  <xsl:if test='Genre = "Sci-Fi"'>
    <tr>
      <td><xsl:value-of select="Title" /></td>
      <td><xsl:value-of select="Genre" /></td>
      <td><xsl:value-of select="Year" /></td>
    </tr>
  </xsl:if>
</xsl:for-each>

The test attribute of the xsl:if tag holds the conditional expression. We’re comparing the Genre value in the XML against the string “Sci-Fi”. If its a match we’ll execute the code inside the tag, otherwise we won’t show anything and we’ll skip the data. Be careful when using string values in XSL, you can’t nest double quotes. Using “Genre = “Sci-Fi”" will cause an error. You can use “Genre = "Sci-Fi&quote”, but I don’t think thats as easy to read. The xsl:if tag can use the following comparisons: = for equals, != for not equals, > for greater than, and < for less than.

Here is the full XSL file and the filtered results.

The xsl:if statement isn’t extremely useful, since it only deals with comparisons to a single value. If you need to check against multiple values or take a different action if there is no match it can’t help you. To do those things you need to use the xsl:choose tag.

The xsl:choose works a lot like the switch statement in PHP. You can provide one or more comparisons and then a catchall to handle any that don’t match. Lets say we’d like to have the Sci-Fi genre be in black, thrillers in yellow, and everything else plain white. We’d add a xsl:choose tag like this:
<xsl:choose>
  <xsl:when test='Genre="Sci-Fi"'>
    <td style="background:black; color:white;"><xsl:value-of select="Genre" /></td>
  </xsl:when>
  <xsl:when test='Genre="Thriller"'>
    <td style="background:yellow;"><xsl:value-of select="Genre" /></td>
  </xsl:when>
  <xsl:otherwise>
    <td><xsl:value-of select="Genre" /></td>
  </xsl:otherwise>
</xsl:choose>

Each comparison you’d like to make is placed in the test attribute of a xsl:when tag nested inside the xsl:choose tag. You can have as many of those as you need. The code in the first xsl:when tag with a true conditional will run, all the others will be skipped. The xsl:otherwise tag is the catchall that will be used if none of the comparisons is true, its contents will be executed only if none of the xsl:when tags are executed.

I’ve included the xsl:choose into this XSL file. Combining it with the XML data gives us this result. Now we’re able to use XSLT to convert XML data into a web, and customize the appearance so that everything looks nice on screen.

However, that xsl:for-each loop is starting to look a little cluttered. XSLT provides a way that we can move some of that code out. We do that by using by creating additional xsl:template tags. Each one will be responsible for transforming part of the XML data.

We’ll move the conditional formatting for the Genre value into its own xsl:template tag and inside of the xsl:for-each tag we’ll reference it using an xsl:apply-templates tag.

The xsl:for-each now looks like this:
<xsl:for-each select="List/Movie">
<xsl:sort select="Title" />
  <tr>
    <td><xsl:value-of select="Title" /></td>
    <xsl:apply-templates select="Genre" />
    <td><xsl:value-of select="Year" /></td>
  </tr>
</xsl:for-each>

The select attribute of the xsl:apply-templates tag specifies which part of the XML we’re going to handle with another template. We’ll need to create a new template using that exact string, and that looks like this:
<xsl:template match="Genre">
  <xsl:choose>
    <xsl:when test='. = "Sci-Fi"'>
      <td style="background:black; color:white;"><xsl:value-of select="." /></td>
    </xsl:when>
    <xsl:when test='. = "Thriller"'>
      <td style="background:yellow;"><xsl:value-of select="." /></td>
    </xsl:when>
    <xsl:otherwise>
      <td><xsl:value-of select="." /></td>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

As you can see the match attribute for this xsl:template tag has the same text as the select attribute of the xsl:apply-template tag. One thing that may not be clear is that the XML data path will be the path to the value listed in the xsl:apply-templates tag. In this case the path in the xsl:for-each loop was /List/Movie, the xsl:apply-templates tag added Genre to it; so the path that the xsl:template tag is using is /List/Movie/Genre which is the exact value that we’d like to work with. When we’re attempting to write that value we need to use a path of . which translates into the current value. If we tried to use a path of Genre, like we did in the loop, it would attempt to retrieve a value from /List/Movie/Genre/Genre and there aren’t tags nested that deep in our data.

I’ve included the xsl:choose into this XSL file. Combining it with the XML data gives us this result. In some cases additional xsl:templates will make the code easier to read, in others it might make it harder since you have to jump around more to find out what is really going on. Depending on your coding style and what you are trying to accomplish you can use as many, or as few, as you need.

One Response to “XSLT Tutorial”

  1. 1
    murali Says:

    nice one

Leave a Reply

© 2007 Mindlence