Read XSLT 2.0 and XPath 2.0 Programmer's Reference, 4th Edition Online
Authors: Michael Kay
If there is a document referenced from the stylesheet, for example to hold look-up data such as messages or tax rates, it is also useful to define this in a global variable, for example:
Variables to Hold Intermediate Results
When a complex transformation task is undertaken, it is often useful to split it into a sequence of steps. The output of one step can be held in a variable, which is then used as the input to the next step.
Any kind of value can be used as an intermediate result, but most commonly it will either be a sequence of items, or a temporary document.
The value of a variable will be a temporary document (or a result tree fragment as it was known in XSLT 1.0) if it is defined using the content of the
select
attribute, and if there is no
as
attribute.
Variables evaluated using a sequence constructor are useful when you use
document()
function. This greatly increases their usefulness. There are two main categories:
It's often useful to break up a transformation into a sequence of steps (often referred to as a
pipeline
), to simplify the logic. A temporary tree can be used as the output of the first phase and the input to the next.
For example, if you need to create a data structure such as a list of keyword/value pairs, and pass this as a parameter to a function or template, then the best way to construct this data structure is as a tree.
The next example shows a multiphase transformation.
Example: A Multiphase Transformation
This example performs a transformation in two phases. The first phase starts with a list containing the results of a series of soccer matches, and computes a league table showing the standing of the various teams. The second phase renders this league table in HTML. These are quite separate operations and it's best to keep them separate. In fact, we'll keep them completely separate by using different stylesheet modules and different modes.
Source
The source document is
soccer.xml
. It contains the results of individual matches. Here are the first few:
…
Stylesheet
The first phase of the transformation calculates a league table. This is in module
league.xsl
, shown below:
xmlns:xsl=“http://www.w3.org/1999/XSL/Transform”
version=“2.0”
>
select=“count($matches[team[.=$this]/@score gt
team[.!=$this]/@score])”/>
select=“count($matches[team[.=$this]/@score lt
team[.!=$this]/@score])”/>
select=“count($matches[team[.=$this]/@score eq
team[.!=$this]/@score])”/>
select=“sum($matches/team[.=current()]/@score)”/>
select=“sum($matches[team=current()]/team/@score) - $for”/>
lost=“{$lost}” for=“{$for}” against=“{$against}”/>
Since we're talking about usage of
$this
is set to the context item so that it can be used within predicates, where the context item will have changed. Then the computation is done entirely within a sequence of local variable declarations. This is a very characteristic programming style. Finally, the template outputs one
The second stylesheet
show-league.xsl
renders the league table into HTML. It's quite straightforward:
xmlns:xsl=“http://www.w3.org/1999/XSL/Transform”
version=“2.0”
>
Note the variable here to capture the result of the first phase of processing. The value of this variable will be a document node containing the
League Table
Team Played Won Lost Drawn For Against
Output
The output of the stylesheet is shown in
Figure 6-19
.
Avoiding Trivial Documents
There is a tendency to create temporary documents when they aren't needed. One often sees constructs like this:
This first calculates a number (the result of the
count()
function). It then converts this number to a string, wraps the string into a text node, creates a document node with the text node as its only child, and sets the value of the variable to be this document node. The chances are that the variable will then be used in an expression such as
$played>5
, in which a number is required: so the system has to atomize the document node, extract its string value by reading all its text node descendants, and then convert the resulting string to a number. You can save yourself two lines of code, and the system a lot of unnecessary work, by writing instead:
Here the value of the variable is a number. No need to create the temporary document, and no need to atomize it later to extract the value.
I call this kind of variable a trivial document. A trivial document behaves almost exactly like an
untypedAtomic
value. Here is a slightly more complicated example:
In XSLT 2.0 it is almost always possible to avoid this kind of construct and the overheads that go with it. Write this instead as:
select=“if (@width) then @width else 0”/>
Or more concisely:
There are several benefits in declaring the type. Firstly, it's good documentation for anyone reading the stylesheet. Secondly, it causes the system to do some error checking for you: if the width isn't an integer, you”ll get an appropriate error message. Finally, it's likely to be more efficient. A document node is a very heavyweight object compared with an integer.