XML, XSL, two of a family of extensible languages
PREVIOUSFIRSTLAST

The XSLT transformation language

Introductory remarks

XSLT transforms an XML input document into a hierarchical result tree, which often is serialised as an XML instance (see figure).

You can also serialise the output as XSL formatting objects, which can be further transformed into a printable or viewable output format (see figure)

Finally, you can also generate non-XML output, that can be interpreted by a dedicated parser (I shall give an example of direct transformation into LaTeX).

The XSLT processor ignored the DTD and any comments and processing instructions present in the input XML document.

The output result tree can be constructed by pulling the data (mainly by the instructions <xsl:value-of> and <xsl:for-each>. This approach worked all right if the structure of the input source is quite static and known.

Alternatively, when the source structure is only partly known it might be better to push the input data, i.e., let the construction of the result tree be driven by the data themselves. Here one can use the <apply-templates> instruction.

In most practical situations a combination of both these approaches is probably a winning strategy.

A simple example

Let us start with the simplest possible stylesheet, an empty one.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/XSL/Transform/1.0">
</xsl:stylesheet>

When we run this stylesheet with one of the XSL processors (xt or lotusxsl), we get the following
> lotusxsl.sh welcome.xml empty.xsl 
========= Parsing and preparing empty.xsl ==========
Parsing and init of empty.xsl took 1516 milliseconds
========= Parsing welcome.xml ==========
Parse of welcome.xml took 244 milliseconds
=============================
Transforming welcome.xml via Input XSL...
transform took 127 milliseconds
Total time took 482 milliseconds
XSLProcessor: done
Welcome to the world of XML!
So we see that the input is just copied through to the output (the Microsoft XSL processors will show nothing since they require that you add a specific template rule for copying input to output.)

We now construct a stylesheet which is a little more complicated for our example:
<?xml version='1.0'?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/XSL/Transform/1.0">

<xsl:output method="html"/>

<xsl:template match="/">
<html>
<em><b><xsl:apply-templates/></b></em>
</html>
</xsl:template>
</xsl:stylesheet>
When we run that with xt we get
> xt welcome.xml welcome.xsl      
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<em><b>Welcome to the world of XML!</b></em>
</html>
as we would expect. If we would not have used the <xsl:apply-templates/> instruction, the data would not have been copied through to the output!

In the above example the data were pushed through to the output, but we could also pull them ourselves (although this example is too trivial to make the point clearly).
<?xml version='1.0'?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/XSL/Transform/1.0">

<xsl:output method="html"/>

<xsl:template match="/">
<html>
<em><b><xsl:value-of select="."/></b></em>
</html>
</xsl:template>
</xsl:stylesheet>

As it is so common that XML files will be transformed into HTML, a special simplified syntax, without explicit templates, is provided (in fact the template for the root node is implicit).
<html xmlns:xsl="http://www.w3.org/XSL/Transform/1.0"
      xmlns="http://www.w3.org/TR/xhtml1">
<em><b><xsl:value-of select="."/></b></em>
</html>
which gives the following output
> xt welcome.xml welcome1.xsl
<html xmlns="http://www.w3.org/TR/xhtml1">
<em><b>Welcome to the world of XML!</b></em>
</html>

The structure of an XSL stylesheet

The various elements that are possible at the top level of a stylesheet are listed below.

<xsl:stylesheet xmlns:xsl="http://www.w3.org/XSL/Transform/1.0">
  <xsl:import href="..."/>
  <xsl:include href="..."/>
  <xsl:strip-space elements="..."/>
  <xsl:preserve-space elements="..."/>
  <xsl:output method="..."/>
  <xsl:key name="..." match="..." use="..."/>
  <xsl:locale name="...">  ...  </xsl:locale>
  <xsl:attribute-set name="...">  ...  </xsl:attribute-set>
  <xsl:variable name="...">...</xsl:variable>
  <xsl:param name="...">...</xsl:param>
  <xsl:template match="...">  ...  </xsl:template>
  <xsl:template name="...">   ...  </xsl:template>
</xsl:stylesheet>

Template rules

The figure shows graphically how a single instruction in the stylesheet obtains information about a particular point in the source tree. Part of the input tree and then copied or transformed and copied to the result tree.

A few examples of patterns follow.

[1]   Pattern                ::=    LocationPathPattern
                                  | Pattern '|' LocationPathPattern
[2]   LocationPathPattern    ::=    '/' RelativePathPattern?
                                  | IdKeyPattern (('/' | '//')
                                    RelativePathPattern)?
                                  | '//'? RelativePathPattern
[3]   IdKeyPattern           ::=    'id' '(' Literal ')'
                                  | 'key' '(' Literal ',' Literal ')'
[4]   RelativePathPattern    ::=    StepPattern
                                  | RelativePathPattern '/' StepPattern
                                  | RelativePathPattern '//'
                                    StepPattern
[5]   StepPattern            ::=    AbbreviatedAxisSpecifier NodeTest
                                    Predicate*

A template rule is specified with the xsl:template element. The match attribute is a Pattern that identifies the source node or nodes to which the rule applies.

The following template rule matches hi elements and has a template, which produces a fo:inline-sequence formatting object with a font-weight property of italic.
<xsl:template match="hi">
  <fo:inline-sequence font-style="italic">
    <xsl:apply-templates/>
  </fo:inline-sequence>
</xsl:template>

The <xsl:apply-templates/> instruction processes all of the children of the current node, including text nodes.

Conflict resolution is handled explicitly (with a priority attribute, or implicitly via a set of rules (which behave as one would expect in most cases). When in doubt, use the priority attribute.

Modes allow an element to be processed multiple times, each time producing a different result. Such a mode can be specified on both xsl:template and xsl:apply-templates instruction via an optional mode attribute.

There is a built-in template rule to allow recursive processing to continue in the absence of a successful pattern match by an explicit template rule in the stylesheet. This template rule applies to both element nodes and the root node.
<xsl:template match="*|/">
  <xsl:apply-templates/>
</xsl:template>

There is a built-in template rule for each mode, which allows recursive processing to continue in the same mode in the absence of a successful pattern match by an explicit template rule in the stylesheet.
<xsl:template match="*|/" mode="m">
  <xsl:apply-templates mode="m"/>
</xsl:template>

Similarly, the built-in template rule for text and attribute nodes just copies text through:
<xsl:template match="text()|@*">
  <xsl:value-of select="."/>
</xsl:template>

The built-in template rule for processing instructions and comments is to do nothing.
<xsl:template match="processing-instruction()|comment()"/>

These built-in template rules have have a lower import precedence than all other template rules. Thus, the author can override a built-in template rule by including an explicit template rule.

The list of XSLT functions, with their type, argument(s), and the section where they are defined in the (August 1999) XSLT WD is given.

node-set current()
returns node-set with current element as only node (Section 13.4).
node-set document(object,node-set?)
handles multiple source documents (Section 13.1).
format-number(number,string,string?)
handles formatting of numbers (Section 13.3).
boolean extension-element-available(string)
returns true if XSLT processor has implementation for named extension element (Section 14.1).
boolean extension-function-available(string)
returns true if XSLT processor has implementation for named extension function (Section 14.1).
string generate-id(node-set?)
returns string that uniquely identifies a node (Section 13.4).
node-set key(string,object)
handles document keys (Section 13.2).
object system-property(string)
returns value of system property (Section 13.4).
string unparsed-entity-uri(string)
returns URI of unparsed entity (Section 13.4).

Looking at more complicated examples

Suppose you have the following input fragment:
<chapter id="H1Introduction">
<stitle>Introduction</stitle>

<section>
<stitle>What is XML?</stitle>
and the following templates in the stylesheet
<xsl:template match="chapter/stitle">
  <xsl:call-template name="sectionhead">
    <xsl:with-param name="level">1</xsl:param>
  </xsl:call-template>
</xsl:template>

<xsl:template match="section/stitle">
  <xsl:call-template name="sectionhead">
    <xsl:with-param name="level" select="2"/>
  </xsl:call-template>
</xsl:template>
The named template sectionhead is defined as follows (it shows how template variables are used and transmitted):
<xsl:template name="makeheading">
  <xsl:choose>
    <xsl:when test="ancestor::appendix">
      <xsl:number count="chapter" format="A."/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:number format="1." count="chapter"/>
    </xsl:otherwise>
  </xsl:choose>
  <xsl:choose>
    <xsl:when test="ancestor::section">
      <xsl:number level="multiple" 
                  count="section|subsection"
                  format="1.1. "/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:text> </xsl:text>
    </xsl:otherwise>
  </xsl:choose>
  <xsl:apply-templates/>
</xsl:template>

<xsl:template name="sectionhead">
  <xsl:param name="level" select="1"/>
  <xsl:element name="H{$level}">
  <xsl:choose>    
    <xsl:when test="../@id">
      <A NAME="{../@id}">
      <xsl:call-template name="makeheading"/>
      </A>
    </xsl:when>
    <xsl:otherwise>
      <xsl:call-template name="makeheading"/>
    </xsl:otherwise>
  </xsl:choose>
  </xsl:element>
</xsl:template>


Last updated: September 10th 1999