5.1. XSL Transformations

A template to transform a tree node has the following form:

<xsl:template match="pattern expr">
    value replacing the matching node(s)   
</xsl:template>

Like with XML Schemas, we must distinguish between the XSLT predefined elements and the elements used to create the document. The namespace xsl is most often used for XSLT elements. In order to trigger a xsl:template (a production rule), the transformation process must first identify the node (or nodes) to which it applies. This is done with the value of the match attribute that specifies an pattern expression, a somewhat restricted type of XPath expression. The content of the xsl:template defines the structure of the produced tree by combining any part of the matched tree, new parts or even other parts of the source document tree. The parts of the tree used as building blocks are referenced by XPath expressions and combined with functions, conditions, restricted looping constructs, etc. But the reader must remember that XSLT is a declarative language (similar to Prolog in some ways), so the ordering of templates cannot be reliably used to influence the order of processing of the document tree.

A stylesheet follows a simple process: find a node for which a template applies and then, according to the content of the template, build a new tree structure in the context of this node. A context gives access to the current node, its parent, its siblings and its position within its siblings. To build the new tree structure, a template usually involves the application of templates to children of the current node and their combination. This is done with the xsl:apply-templates element; without attributes, this forces the application of templates to all the children element nodes of the current node, but the transformation can be applied to other nodes by using the select attribute which specifies an XPath expression.

Templates can also be named and called with xsl:call-template, similar to procedures in standard programming languages. But be aware that these procedures are rules and that they cannot have variables that can change their value. XSLT is thus a single assignment language much like functional languages; parameters are the only mean of passing variable information between templates. Contrarily to ordinary templates, named templates do not change the context of their application. Therefore, we see that the principles underlying XSLT are general, simple and powerful.

Table 5.1 shows the main stylesheet elements for defining transformation rules. As a stylesheet is itself an XML document, it can be validated with the appropriate schema. The root element is xsl:stylesheet, which contains a certain number of templates.

xsl:template with a match attribute will be called when an element matching its pattern is encountered during the processing of the XML instance document. A xsl:template element with a name attribute must be called explicitly by a xsl:call-template. The content of the matched element in the source document is replaced with the content of the template, which usually involves the application of templates to the children of the current node. Formal parameters are declared at the start of an xsl:template element with xsl:param elements.

A xsl:function element with a name attribute and xsl:param elements can be used to define an XPath function. User-defined function must be declared in a separate namespace in order to differentiate them from the predefined system functions. Even though, user-defined function formal parameters are declared with a xsl:param elements, the actual parameters are given within parenthesis in the order of the declaration of the xsl:param elements. Of course, the name of the user-defined function call must be preceded by the namespace prefix corresponding to the namespace used in the declaration.

xsl:apply-templates is the fundamental operation for traversing the document tree. Without attributes, it indicates that processing should be recursively applied to all its children elements and text nodes (not to its attributes). If the element is empty, then the nodes are processed in the document order but it is possible to specify a different ordering with a xsl:sort element. Actual parameters can also be given by name and value using with-param elements.

xsl:value-of is the fundamental way of getting the string value contained in an element from the source document. xsl:copy-of should be used to get the whole tree value.

A local single assignment variable can be defined within templates with an xsl:variable element. Its string value can then be recovered in an XPath expression by prefixing its name with $. If the whole tree value is needed we can use xsl:copy to get a reference to the original value and xsl:copy-of to get a new copy.

Conditional processing can be achieved with xsl:if which returns its content when the value of its test attribute is true or a non-empty set of nodes. Also xsl:choose can select the first value from a series of alternatives indicated by xsl:when. The xsl:when conditions are tested in sequence and the first one that returns true is the value of this element. If no test succeeds and an xsl:otherwise element is present, its value is the value of the xsl:choose element.

Although recursive traversal of the document tree is the preferred way of going through nodes, it is also possible to do this traversal iteratively with a xsl:for-each and xsl:for-each-group. These templates are especially convenient for transforming a sequence of children nodes at all the same levels, for example to create tables or summary information.

Node processing is usually conducted in document order but the order can be changed using xsl:sort, which allows to specify the sorting key with the select attribute and an ascending or descending sort according to the value of order attribute. The elements are usually sorted by their text value but their string value can also be used when sorting by specifying a data-type attribute.

In the XPath 1.0, the expression language was quite limited so all control structures (function definition and calls, alternative or loop constructs) for the computation of values were the ones of XSLT. But as we have seen in the previous chapter, with XPath 2.0 we now have the choice between xsl:if, xsl:choose and xsl:for-each and the if () then ... else and for constructs of XPath. User-defined functions of XPath 2.0 or XSLT named templates can also be sometimes used interchangeably.

In most cases, this choice is a matter of personal choice but if the function has to be used in other XPath expressions then it should be written as a function and not as a named template. Within an XPath function, then the if and for XPath 2.0 must be used. If the function or expression takes atomic values (string or a number) as input and returns an atomic value, then using an XPath 2.0 expression or function is more convenient than using a template. Named templates are most useful for cases when there are default values, when named parameters are useful or when the current context is needed. In the latter case, the current node would have to passed as parameter to an XPath function because an XPath function does not execute in a given context. In practice with XSLT 2.0, templates are almost exclusively used for producing results that will appear in the output document. Functions and expressions are more convenient when their result is used by other parts of the program.

Although XPath expressions can be used to reference any node or sequence of nodes in an XML file, it is often convenient and much more efficient to have a direct access to any node in a document. The xsl:key element is a top-level declaration indicating to the XSLT processor that a direct access is needed for nodes matching a given pattern. This declaration usually implies the building of an index or hash table that allows a direct matching between a key and a set of nodes matching it. A single key can refer to a many nodes and a single node can be referred by many keys. Access to the nodes is achieved with the XPath key function to which the key value is given. As there can be many key declarations in a program, the key function must also be given in which index the search is to be made.

The dynamic creation of target document elements, attributes and text nodes is performed using the xsl:element, xsl:attribute and xsl:text elements. xsl:attribute-set is useful for grouping many attributes under a single name and to combine them.

xsl:message is a very convenient tracing device which writes its content either on the standard output or another device. It has no effect on the resulting value.

We will now look at examples of these principles and XSL elements in the following sections. First with straightforward transformations into HTML, then into plain text and finally into formatting objects to produce more complex formatting.

Table 5.1. XSLT syntax reminder

<xsl:stylesheet>
    xsl:import*, 
    (declaration|xsl:variable|xsl:param)*
</xsl:stylesheet>
<xsl:template match="pattern" name="QName">
    xsl:param*, sequence-constructor*
</xsl:template>
<xsl:param name="QName" select="expression">
    sequence-constructor
</xsl:param>
<xsl:apply-templates select="expression">
    (xsl:sort*|xsl:with-param)*
</xsl:apply-templates>
<xsl:call-template name="Qname"/>
<xsl:with-param name="QName" select="expression">
    sequence-constructor
</xsl:with-param>
<xsl:function name="QName">
    xsl:param*, sequence-constructor*
</xsl:function>
<xsl:value-of select="expression">
    sequence-constructor
</xsl:value-of>
<xsl:variable name="QName" select="expression">
    sequence-constructor
</xsl:variable>
<xsl:copy>
    sequence-constructor
</xsl:copy>
<xsl:copy-of select="expression"/>
<xsl:if test="expression">
    sequence-constructor
</xsl:if>
<choose>
    xsl:when*, xsl:otherwise?
</choose>
<xsl:when test="expression">
    sequence-constructor
</xsl:when>
<xsl:otherwise>
    sequence-constructor
</xsl:otherwise>
<xsl:for-each select="expression">
    xsl:sort*, sequence-constructor
</xsl:for-each>
<xsl:for-each-group select="expression" group-by="expression">
    xsl:sort*, sequence-constructor
</xsl:for-each-group>
<xsl:sort select="expression" data-type="{string}">
    sequence-constructor
</xsl:sort>
<xsl:key name="qname" match="pattern"
 use="expression">
    sequence-constructor
</xsl:key>
<xsl:element name="{string}">
    sequence-constructor
</xsl:element>
<xsl:text>
    character data
</xsl:text>
<xsl:attribute name="{string}" select="expression">
    sequence-constructor
</xsl:attribute>
<xsl:attribute-set name="QName" use-attribute-sets="Qnames">
    xsl:attribute*
</xsl:attribute-set>
<xsl:message>
    sequence-constructor
</xsl:message>

A reminder of the subset of the XSLT syntax used in our examples. Names in italics refer to other elements. Regular expression syntax is explained in Table B.1