10.7. Templates and Patterns
An XSLT style sheet transforms an XML document by applying
templates for a given type of node. A template
element looks like this:
<xsl:template match="pattern">
...
</xsl:template>
where pattern selects the type of node to
be processed.
For example, say you want to write a template to transform a
<para> node (for paragraph) into HTML. This
template will be applied to all <para>
elements. The tag at the beginning of the template will be:
<xsl:template match="para">
The body of the template often contains a mix of template
instructions and text that should appear literally in the result,
although neither are required. In the previous example, we want to
wrap the contents of the <para> element in
<p> and </p> HTML
tags. Thus, the template would look like this:
<xsl:template match="para">
<p><xsl:apply-templates/></p>
</xsl:template>
The <xsl:apply-templates/> element
recursively applies all other templates from the style sheet against
the <para> element (the current node) while
this template is processing. Every style sheet has at least two
templates that apply by default. The first default template processes
text and attribute nodes and writes them literally in the document.
The second default template is applied to elements and root nodes
that have no associated namespace. In this case, no output is
generated, but templates are applied recursively from the node in
question.
Now that we have seen the principle of templates, we can look at a
more complete example. Consider the following XML document:
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE text SYSTEM "example.dtd">
<chapter>
<title>Sample text</title>
<section title="First section">
<para>This is the first section of the text.</para>
</section>
<section title="Second section">
<para>This is the second section of the text.</para>
</section>
</chapter>
To transform this into HTML, we use the following template:
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html"/>
<xsl:template match="chapter">
<html>
<head>
<title><xsl:value-of select="title"/></title>
</head>
<body>
<xsl:apply-templates/>
</body>
</html>
</xsl:template>
<xsl:template match="title">
<center>
<h1><xsl:apply-templates/></h1>
</center>
</xsl:template>
<xsl:template match="section">
<h3><xsl:value-of select="@title"/></h3>
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="para">
<p><xsl:apply-templates/></p>
</xsl:template>
</xsl:stylesheet>
Let's look at how this style sheet works. As
processing begins, the current node is the document root (not to be
confused with the <chapter> element, which
is its only descendant), designated as / (like the
root directory in a Unix filesystem). The XSLT processor searches the
style sheet for a template with a matching pattern in any children of
the root. Only the first template matches (<xsl:template
match="chapter">). The first template is then applied to
the <chapter> node, which becomes the
current node.
The transformation then takes place: the
<html>, <head>,
<title>, and <body>
elements are simply copied into the document because they are not XSL
instructions. Between the tags <head> and
</head>, the <xsl:value-of
select="title"/> element copies the contents of the
<title> element into the document. Finally,
the <xsl:apply-templates/> element tells the
XSL processor to apply the templates recursively and insert the
result between the <body> and
</body> tags.
This time through, the title and section templates are applied
because their patterns match. The title template inserts the contents
of the <title> element between the HTML
<center> and <h1>
tags, thus displaying the document title. The section template works
by using the <xsl:value-of select="@title">
element to recopy the contents of the current
element's title attribute into
the document produced. We can indicate in a pattern that we want to
copy the value of an attribute by placing the at symbol
(@) in front of its name.
The process continues recursively to produce the following HTML
document:
<html>
<head>
<title>Sample text</title>
</head>
<body>
<center>
<h1>Sample text</h1>
</center>
<h3>First section</h3>
<p>This is the first section of the text.</p>
<h3>Second section</h3>
<p>This is the second section of the text.</p>
</body>
</html>
As you will see later, patterns are XPath expressions for locating
nodes in an XML document. This example includes very basic patterns,
and we have only scratched the surface of what can be done with
templates. More information will be found in Section 10.9
later in this chapter.
In addition, the <xsl:template> element has
a &mode; attribute that can be used for
conditional processing. An <xsl:template
match="pattern"
mode="mode">
template is tested only when it is called by an
<xsl:apply-templates
mode="mode">
element that matches its mode. This functionality can be used to
change the processing applied to a node dynamically.
10.7.1. Parameters and Variables
To finish up with templates, we should discuss the
name attribute. These templates are similar to
functions and can be called explicitly with the
<xsl:call-template
name="name"/>
element, where name matches the name of
the template you want to invoke. When you call a template, you can
pass it parameters. Let's assume we wrote a template
to add a footer containing the date the document was last updated. We
could call the template, passing it the date of the last update this
way:
<xsl:call-template name="footer">
<xsl:with-param name="date" select="@lastupdate"/>
</xsl:call-template>
The call-template declares and uses the parameter
this way:
<xsl:template name="footer">
<xsl:param name="date">today</xsl:param>
<hr/>
<xsl:text>Last update: </xsl:text>
<xsl:value-of select="$date"/>
</xsl:template>
The parameter is declared within the template with the
<xsl:param name="date"> element whose
content (today) provides a default value. We can
use this parameter inside the template by placing a dollar sign
($) in front of the name.
We can also declare variables using the <xsl:variable
name="name">
element, where the content of the element gives the variable its
value. The variables are used like parameters by placing a dollar
sign ($) in front of their names. Note that even
though they are called variables, their values are constant and
cannot be changed. A variable's visibility also
depends on where it is declared. A variable that is declared directly
as a child element of <xsl:stylesheet> can
be used throughout the style sheet as a global variable. Conversely,
when a variable is declared in the body of the template, it is
visible only within that same template.
10.7.2. Style Sheet Import and Rules of Precedence
Style sheets may be imported using the <xsl:import
href=
"uri">
element, where the &href; attribute indicates
the path of the style sheet to be imported. Note that an
<xsl:import> statement must be a direct
child of the <xsl:stylesheet> element.
Imported style sheet templates have lower precedence than templates
contained in the file into which they are incorporated. This means
that if two templates compete for the processing of an element, the
template of the original file takes precedence over the template of
the imported file. Thus, imported templates can be overridden by
redefining them in the original style sheet.
The rules of precedence can be changed in two ways:
-
The <xsl:apply-imports/> element can be used
to give imported templates precedence in the body of a template.
-
The
priority="level"
attribute can be given in the opening
<xsl:template> tag. Therefore, the level of
precedence defined for the template is a real number. The larger the
number, the more precedence the template has. A value of +1 ensures
that the template has precedence over other templates for which no
precedence has been defined (0 is the default). A value of -1
guarantees that any other unprioritized template has precedence.
Priority values overrule import precedence.
Style sheets can also be included in an XSL file with the
<xsl:include
href="uri"/>
element. The precedence of an included template is the same as that
of the calling style sheet templates.
10.7.3. Loops and Tests
To process an entire list of elements at the same time, use the
<xsl:for-each> loop. For example, the
following template adds a table of contents to our example:
<xsl:template name="toc">
<xsl:for-each select="section">
<xsl:value-of select="@title"/>
<br/>
</xsl:for-each>
</xsl:template>
The body of this <xsl:for-each> loop
processes all the <section> elements that
are children of the current node. Within the loop, we output the
value of each section's title
attribute, followed by a line break.
XSL also defines elements that can be used for tests:
- <xsl:if test="expression">
-
The body of this element is executed only if the test expression is
true.
- <xsl:choose>
-
This element allows for several possible conditions. It is comparable
to switch in the C and Java languages. The
<xsl:choose> element is illustrated as
follows:
<xsl:choose>
<xsl:when test="case-1">
<!-- executed in case 1 -->
</xsl:when>
<xsl:when test="case-2">
<!-- executed in case 2 -->
</xsl:when>
<xsl:otherwise>
<!-- executed by default -->
</xsl:otherwise>
</xsl:choose>
The body of the first <xsl:when> element
whose test expression is true will be executed. The XSL processor
then moves on to the instructions following the closing
</xsl:choose> element tag, skipping the
remaining tests. The <xsl:otherwise> element
is optional; its body is executed only if none of the preceding
elements were executed.
10.7.4. Numbering Elements
XSL provides a simple method for numbering elements with the
<xsl:number> element. Let's
assume we want to number the sections and paragraphs in a document.
We can do this by adding the following code before displaying the
section titles and the content of the paragraphs:
<xsl:number count="sect|para"
level="multiple" format="1.1"/>
<xsl:text>- </xsl:text>
The result is:
1 - First section
1.1 - This is the first section of text.
2 - Second section
2.1 - This is the second section of text.
The count attribute decides which elements should
be numbered. Elements must be separated by a |.
The level attribute specifies the level of
numbering and may take one of three string values:
single, multiple, or
any. single tells the processor
to number only one level. In this case, paragraph numbers will not
indicate the section number. multiple numbers
several levels, meaning that the first part of the paragraph number
is the section number in our previous example. any
tells the processor to add numbering without regard to level. Here,
the numbers of the sections and paragraphs are consecutive.
The format attribute indicates the style of numbering. Letters or
numbers may be used, with a separator in between. The letters may be
A or a (for alphabetical
numbering in upper- or lowercase), I or
i (for numbering in upper- or lowercase Roman
numerals), or 1 (for numbering in Arabic
numerals). For example, to number sections with Roman numerals and
paragraphs with lowercase letters, use this format attribute:
format="I.a"
10.7.5. Output Method
An XSLT processor can be instructed to produce a specific type of
output with the <xsl:output/> element. For
example, <xsl:output method="html"/> causes
the processor to execute certain transformations needed for the
resulting document to be valid HTML. Specifically, it transforms
empty tags. For example, the XML <hr/> tag
is converted to the HTML <hr> tag (for
horizontal rules) without a closing slash.
It is also possible to indicate an XML output method
(method="xml"), where the XSLT processor adds the
standard XML header (<?xml version="1.0"?>).
It may seem strange to produce an XML document from another XML
document, yet it is often helpful to convert a document from one DTD
to a valid document for another DTD. Thus, XSLT is also a language
for inter-DTD conversions.
Finally, you can specify a text output method
(method="text") to produce pure text. XSLT has
built-in outputs for XML, HTML, and text, but some processors may
support other output methods (sometimes identified by URLs).
We should point out that when you choose the HTML or XML output
method, the processor may remove or rearrange whitespace in blocks of
text (spaces, tabs, and carriage returns). However, there are several
solutions for preserving whitespace. The first is to indicate the
list of elements to be preserved in the
<xsl:preserve-space
elements="list">
element. The second is to add the
&indent="no"; attribute to the
<xsl:output> element to indicate that you do
not want the resulting document to be indented. Note, however, that
spaces are no longer preserved in <xsl:text>
elements where content is written as-is in the resulting document. No
indenting is produced for the text output method.
 |  |  | 10.6. XSLT Style Sheet Structure |  | 10.8. XSLT Elements |
Copyright © 2003 O'Reilly & Associates. All rights reserved.
|