Introducing XSL Transformations

Table of Contents

A Simple Stylesheet
A More Detailed Look
Outputting Markup
Tracking the current node

CSS is a good way of laying out your pages in a well-designed manner directly from the XML document. And it provides a way for you to allow most browsers to view your XML without falling over or drawing a blank. But CSS is very limited in what it can do to the data before it is shown on screen. And it certainly can't manipulate the data in any real way, or rewrite it into other markup languages. That's where XSL steps in.

The XSL standard comprises two basic parts:

XSL Transformations (XSLT)

These are widely used, well-defined and stable as a standard, and are used primarily to rewrite XML into other XML applications (such as XHTML or WML).

XSL Formatting Objects (XSL-FO)

This standard is used to generate formatted output such as PDF files; they provide exact control of layout and presentation that isn't possible with either XSLT or CSS.

These two languages share many features, but are designed to achieve different tasks. XSLT is a general-purpose language for transforming one XML-based markup language into another, whereas XSL-FO is designed specifically for creating views of XML data in paged-media formats. Over the next couple of weeks, we're going to look at XSLT in depth, and maybe have a quick glance at XSL-FO if there's time.

A Simple Stylesheet

Let's begin by looking at our jokebook example to see why XSLT could be useful. One task that we might want to achieve is to produce an XHTML version of our jokes, for display on the web. XSLT is designed to this this job, as it can transform one XML-based markup language into any other.

Figure 1. The XSLT Processing Model

The XSLT stylesheet is applied using an XSLT processor (such as the ones built into the Oxygen editor), and converts documents from one XML application into another. CSS can still be applied to the resulting XML document.


To perform this transformation, we need to write stylesheets (though they are rather different from the CSS stylesheets we'e already examined). The stylesheet defines a set of templates that can be applied to the XML data document, and which contain rules to manipulate that data into the target format.

XSLT stylesheets are, of course, written as well-formed valid XML, so the basic syntax rules apply. They are usually a mixture of markup languages, including elements from the target language(s), together with statements from the XSLT language itself.

Here's a subset of a simple example:

Example 13. A simple XSLT stylesheet for jokebook (excerpt)

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/TR/xsl">
 
  <xsl:template match="jokebook">
	<html>
	  <head>
		<title>My Jokes</title>
	  </head>
	  <body>
		<h1>My Jokes</h1>
		<xsl:apply-templates />
	  </body>
	</html>
  </xsl:template>

  ....

  <xsl:template match="double-entendre">
	<h3>Double-entendre:</h3>
	<p>The phrase 
	  <xsl:value-of select="phrase" />
	  can mean:</p>
	<ol>
	  <xsl:for-each select="meaning">
		<li>
		  <xsl:value-of />
		</li>
	  <xsl:for-each />
	</ol>

  </xsl:template>
 
  ....

  <xsl:template match="/">
	<xsl:apply-templates />
  </xsl:template>
   
</xsl:stylesheet>
			
			

As you can see, it's written as an XML document, which means it must be well-formed according to the XML rules. The repercussion of this is that any embedded markup in the stylesheet must also be well-formed XML, which in turn means that our results will always be well-formed XML too. This can be difficult to work with at first, but will yield benefits in the quality of markup that is produced.

A More Detailed Look

The first thing to notice in the example is the mixture of XSL and XHTML markup. We're generating an XHTML page from an XML document, so the XHTML markup is inserted at the relevant output stage.

We begin with the declarations:

  <?xml version="1.0"?> 
  <xsl:stylesheet version="1.0" 
      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
			

which define the verion of XML and XSL we're using. The xmlns:xsl clause is a 'namespace' declaration, and defines the standard we're using for the <xsl:_____> tags. We're using the convention of an xsl: prefix, but you could use another prefix such as x: or xslt:.

There are various locations used for this namespace declaration, the above is the current recommendation, though for some processors you may have to change this; for example, the built-in XSLT processor in some versions of IE6 require:

  <xsl:stylesheet xmlns:xsl="http://www.w3.org/TR/WD-xsl">
			

The 'official' declaration, as recommended by the W3C, for an XSLT stylesheet outputting XHTML is:

  <xsl:stylesheet version="1.0"
	xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
	xmlns="http://www.w3.org/TR/xhtml1/strict">
				

Note that this also defines (with the default namespace) the version of XHTML that you're using (the strict version, in this case) within the second namespace (xmlns) declaration. Any markup which has no prefix will thus be XHTML Strict.

The body of the stylesheet consists mostly of <xsl:template> elements, which are designed to match the elements in our source document. So if we have a sequence of <joke> elements in our source document, we can use a <xsl:template match="joke"> element to process each of these with XSLT.

The last section, common to many XSLT 1.0 stylesheets, is the root template declaration:

  <xsl:template match="/"> 
    <xsl:apply-templates /> 
  </xsl:template>
				

This is really just saying that the templates defined in this stylesheet apply to the whole of the XML document tree. If you don't include it, most processors will use a default template which matches this definition, but it's useful to be able to override this default. Most version 1 stylesheets include the root template definition for completeness.

If we examine this more carefully, we can see that we're defining a new template within the <xsl:template> which matches a particular node or set of nodes in the document tree, in this case the topmost or root node. ("/"), which is the container for the entire XML document. We're then defining this template to merely look for and process other templates for the child nodes of the document.

[Warning] XSLT Version 1.0 or 2.0?

On recent versions of the Oxygen editor, you'll be asked to choose between XSLT 1.0 and 2.0 when creating a new stylesheet. At this stage, I'd strongly recommend creating XSLT 1.0, as it's simpler and has fewer “gotchas” than 2.0, which will cause much head-scratching!

Once you're confident with 1.0, then you can take the step towards the newer standard. We'll look next week at some of the new features and complications introduced with XSLT 2.0.

Outputting Markup

In the stylesheet shown in Example 13, “A simple XSLT stylesheet for jokebook (excerpt)”, each template (i.e. within the <xsl:template> elements) could also include XHTML markup, since that's what we're outputting to our result document in this case.

The template for 'jokebook', which is the root element of our XML file in each case, is defined first.

  <xsl:template match="jokebook">
	<html>
	  <head>
		<title>My Jokes</title>
	  </head>
	  <body>
		<h1>My Jokes</h1>
		<xsl:apply-templates />
	  </body>
	</html>
  </xsl:template>
   

Here, we're adding some structural XHTML markup which will form the basis of the XHTML result document, and in the midst of this asking the processor to apply any relevant templates for the contents of the <jokebook> tag, which is achieved with the <xsl:apply-templates /> element.

Skip down now to the <double-entendre> template:

  <xsl:template match="double-entendre">
	<h3>Double-entendre:</h3>
	<p>The phrase 
	  <xsl:value-of select="phrase" />
	  can mean:</p>
	<ol>
	  <xsl:for-each select="meaning">
		<li><xsl:value-of /></li>
	  <xsl:for-each />
	</ol>
  </xsl:template>
 

This again outputs some HTML, but then outputs the entire contents of the <phrase> tag using a <xsl:value-of> statement. Note that this will output the whole of the contents of the element specified, so if it contains sub-elements, you'll get the values of all the #PCDATA elements with no surrounding tags splurged into the output.

You can also see an <xsl:for-each> statement which processes each of the <meaning> tags in the structure. We could, in fact, create a new template for <meaning> and do an <xsl:apply-templates>, which would work just as well here. However, sometimes we need to keep count of how many meanings we've processed (say we wanted to print the total number of meanings at the end), and using the <xsl:for-each> will allow us to do this.

The <xsl:value-of /> without a select attribute just means 'output the value of the current node', in this case, the whole of the currently processed <meaning> tag.

There are a number of other processing elements which can be used, together with a way of defining variables for temporarily storing values, which we'll look at in detail next time.

[Important] Hints and Gotchas

As the stylesheet must be well-formed XML, we have to use XHTML-style closing tags to all our HTML markup.

Though it's common to do so, it's not necessary to create a template for every element you've defined in your DTD.

If a template can't be found for a node in the document tree, then the default action is to apply-templates to its contents, or to output the contents if it's a #PCDATA node.

Order of definitions isn't important, except for your own sanity!

Tracking the current node

This illustrates an important concept in XSLT processing. The 'current node' is the element that we're working on at the moment, the context of the statement. The expressions use in referring to nodes (XPath expressions), like URLs in an <a> tag in HTML, can be relative or absolute, though the syntax differs slightly for XSLT.

As you might expect, you can begin an XPath expression with a / to start at the top of the tree, e.g. /jokebook/joke/simplejoke.

A '//' usually means 'all of...' in some way, so:

  • "//" means "all descendents of the root node"

  • ".//" means "all descendents of the current node"

  • "//name" means "any descendents called 'name' in the whole document"

  • "path//name" means "any descendents of 'path' called 'name'"

The @ symbol can be used to refer to an attribute of the current node, so @author is the value of the author attribute of the current node.

These expressions used in referring to parts of the document are part of a standard called 'XPath', which we'll cover in more detail later.