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.