Applying Templates with xsl:apply-templates

By default, an XSLT processor reads the input XML document from top to bottom, starting at the root of the document and working its way down using preorder traversal. Template rules are activated in the order in which they match elements encountered during this traversal. This means a template rule for a parent will be activated before template rules matching the parent's children.

However, one of the things a template can do is change the order of traversal. That is, it can specify which element(s) should be processed next. It can specify that an element(s) should be processed in the middle of processing another element. It can even prevent particular elements from being processed. In fact, Examples Example 8-4 through Example 8-6 all implicitly prevent the child elements of each person element from being processed. Instead, they provided their own instructions about what the XSLT processor was and was not to do with those children.

The xsl:apply-templates element makes the processing order explicit. Its select attribute contains an XPath expression telling the XSLT processor which nodes to process at that point in the output tree.

For example, suppose you wanted to list the names of the people in the input document; however, you want to put the last names first, regardless of the order in which they occur in the input document, and you don't want to output the professions or hobbies. First you need a name template that looks like this:

<xsl:template match="name">
  <xsl:value-of select="last_name"/>,
  <xsl:value-of select="first_name"/>
</xsl:template>

However, this alone isn't enough; if this were all there was in the stylesheet, not only would the output include the names, it would also include the professions and hobbies. You also need a person template rule that says to apply templates to name children only, but not to any other child elements like profession or hobby. This template rule does that:

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

Example 8-7 shows the complete stylesheet.

Example 8-7. A simple XSLT stylesheet that uses xsl:apply-templates

<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
     
  <xsl:template match="name">
    <xsl:value-of select="last_name"/>,
    <xsl:value-of select="first_name"/>
  </xsl:template>
     
  <xsl:template match="person">
    <xsl:apply-templates select="name"/>
  </xsl:template>
     
</xsl:stylesheet>

When an XSLT processor applies this stylesheet to Example 8-1, this is output:

<?xml version="1.0" encoding="utf-8"?>
     
  Turing,
    Alan
     
  Feynman,
    Richard

The order of the template rules in the stylesheet doesn't matter. It's only the order of the elements in the input document that matters.

Applying templates is also important when the child elements have templates of their own, even if you don't need to reorder the elements. For example, let's suppose you want a template rule for the root people element that wraps the entire document in an HTML header and body. Its template will need to use xsl:apply-templates to indicate where it wants the children of the root element to be placed. That template rule might look like this:

<xsl:template match="people">
  <html>
    <head><title>Famous Scientists</title></head>
    <body>
      <xsl:apply-templates select="person"/>
    </body>
  </html>
</xsl:template>

This template tells the XSLT processor to replace every people element in the input document (of which there is only one in Example 8-1) with an html element. This html element contains some literal character data and several literal result elements of which one is a body element. The body element contains an xsl:apply-templates element telling the XSLT processor to process all the person children of the current people element and insert the output of any matched templates into the body element of the output document.

If you'd rather apply templates to all types of children of the people element, rather than just person children, you can omit the select attribute as demonstrated in Example 8-8. You can also use more complex XPath expressions (discussed in the next chapter) to be more precise about which elements you want to apply templates to.

Example 8-8. An XSLT stylesheet that generates a complete HTML document

<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
     
  <xsl:template match="people">
    <html>
      <head><title>Famous Scientists</title></head>
      <body>
        <xsl:apply-templates/>
      </body>
    </html>
  </xsl:template>
     
  <xsl:template match="name">
    <p><xsl:value-of select="last_name"/>,
    <xsl:value-of select="first_name"/></p>
  </xsl:template>
     
  <xsl:template match="person">
    <xsl:apply-templates select="name"/>
  </xsl:template>
     
</xsl:stylesheet>

When an XSLT processor applies this stylesheet to Example 8-1, it outputs the well-formed HTML document shown in Example 8-9. Look closely at this example, and you may spot an important change that was not explicitly caused by the instructions in the stylesheet.

Example 8-9. The HTML document produced by applying Example 8-8 to Example 8-1

<html>
<head>
<title>Famous Scientists</title>
</head>
<body>
  <p>Turing,
    Alan</p>
  <p>Feynman,
    Richard</p>
</body>
</html>

The difference between Example 8-9 and all the previous output examples is that the text declaration has disappeared! Although there is an XSLT element you can use to specify whether you want a text declaration preceding your output (xsl:output), we haven't used that here. Instead, the XSLT processor noted that the root output element was html, and it adjusted itself accordingly. Since HTML output is such a common case, XSLT has special rules just to handle it. In addition to omitting the text declaration, the processor will use HTML empty-element syntax like <br>, instead of XML empty-element syntax like <br/>, in the output document. (The input document and stylesheet must still be well-formed XML.) There are about half a dozen other changes the XSLT processor may make when it knows it's outputting HTML, all designed to make the output more acceptable to existing web browsers than is well-formed XML.