Mark Wilson I am the creator of TopXML. I am available for international and local (Australia) contracts. I am a Solution Architect/Business Analyst. I have worked in IT in several countries (NZ, Australia, South Africa, UK) building and training teams for government and very large non-governmental organizations. I am ex-Microsoft Consulting Services. I wrote the first book on Microsoft XML published in 2000 called XML Programming with VB and ASP. Most recently I have been building tools for the SEO industry. Ask me for a 37 point SEO health-checkup for your website.
This is an excerpt from
Chapter 4 of the New Riders
book called
Inside XSLT written by Steven Holzner.
PLEASE NOTE: To work with the XSLT and XPath in this
document TopXML recommends you use the demo version of Xselerator
XSL Editor
The following example uses the logical operator >. This rule
applies to all <PLANET> elements after position 5:
<xsl:template match="PLANET[position() > 5]">
<xsl:value-of select="."/>
</xsl:template>
There is also a true function that always returns a value of
true, and a false function that always returns a value of false.
You can also use the not function to reverse the logical sense of
an expression, as in the following case, where I'm selecting all
but the last <PLANET> element:
Finally, the lang function returns true or false depending on
whether the language of the context node (which is given by
xml:lang attributes) is the same as the language you pass to this
function.
In XPath, numbers actually are stored in double floating point
format. (Technically speaking, all XPath numbers are stored in
64-bit IEEE 754 floating-point double format.) All numbers are
stored as doubles, even integers such as 5, as in the example you
just saw:
<xsl:template match="PLANET[position() > 5]">
<xsl:value-of select="."/>
</xsl:template>
You can use several operators on numbers:
+ addition
-
subtraction
* multiplication
div division (the /
character, which stands for division in other languages, is already
heavily used in XML, XSL, and XPath)
mod returns the
modulus of two numbers (the remainder after dividing the first by
the second).
For example, the element <xsl:value-of select="180 +
420"/> inserts the string "600" into the output document. The
following example selects all planets whose day (measured in Earth
days) divided by its mass (where the mass of the earth = 1) is
greater than 100:
<xsl:template match="PLANETS">
<HTML>
<BODY>
<xsl:apply-templates select="PLANET[DAY div MASS >
100]"/>
</BODY>
</HTML>
</xsl:template>
XPath also supports these functions that operate on numbers:
ceiling(). Returns
the smallest integer larger than the number you pass it.
floor(). Returns the
largest integer smaller than the number you pass it.
round(). Rounds the
number you pass it to the nearest integer.
sum(). Returns the
sum of the numbers you pass it.
For example, here's how you can find the average mass of the
planets in planets.xml:
Listing 4.8 Finding average planetary mass
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml"/>
<xsl:template match="PLANETS">
<HTML>
<BODY>
The average planetary mass is:
<xsl:value-of select="sum(child::PLANET/child::MASS) div
count(child::PLANET)"/>
In XPath, strings are made up of Unicode characters, as you'd
expect. A number of functions are specially designed to work on
strings:
string(object
object1). Converts an object into a string.
starts-with(string
string1, string string2). Returns true if the first
string starts with the second string.
contains(string
string1, string string2). Returns true if the first
string contains the second one.
substring(string
string1, number offset, number length). Returns
length characters from the string, starting at
offset.
substring-before(string string1, string string2).
Returns the part of string1 up to the first occurrence of
string2.
substring-after(string string1, string string2).
Returns the part of string1 after the first occurrence of
string2.
string-length(string
string1). Returns the number of characters in
string1.
normalize-space(string string1). Returns string1
after leading and trailing whitespace is stripped and multiple
consecutive whitespace is replaced with a single space.
translate(string
string1, string string2, string string3). Returns
string1 with all occurrences of the characters in
string2 replaced by the matching characters in
string3.
concat(string
string1, string string2,...). Returns all strings
concatenated (that is, joined) together.
And there's another string function you should know about that
is actually part of XSLT, not XPath:
format-number(number
number1, string string2, string string3).
Returns a string holding the formatted string version of
number1, using string2 as a formatting string (you
create formatting strings as you would for Java's
java.text.DecimalFormat method) and string3 as the optional
locale string.
In the following example, I match text nodes whose text starts
with 'E' to match the Earth, and add the text '(the World)' to the
description of Earth, making it 'Earth (the World)'. To do that, I
use the predicate "text()[starts-with(., 'E')]":
XSLT 1.0 added result tree fragments to the data types
supported by XPath. Result tree fragments were just tree fragments
that you could assign to XSLT variables, and never were put to much
use. About all you could do with them was to evaluate their string
value. Support for them was removed in the XSLT 1.1 working draft,
so presumably, they'll no longer be in use in XSLT 2.0.
You can abbreviate predicate expressions by omitting "position()
=" if you like. For example, [position() = 3] becomes [3],
[position() = last()] becomes [last()], and so on. Using the
abbreviated syntax makes XPath expressions in predicates much
easier to use. Here are some examples:
PLANET[2]. Returns the second <PLANET> child of the
context node.
PLANET[last()]. Returns the last <PLANET> child of the
context node.
/PLANETS/PLANET[2]/NAME[1]. Returns the first <NAME>
element of the second <PLANET> element of the <PLANETS>
element.
PLANET[5][@UNITS = "million miles"]. Returns the fifth
<PLANET> child of the context node, only if that child has a
UNITS attribute with value "million miles". Can also be written as
PLANET[@UNITS = "million miles"][5].
That completes this look at the three parts of step patterns:
axes, node tests, and predicates. They are the building blocks of
match patterns. The best way to learn how to create patterns is by
example, and many examples are coming up. First, though, it's
important to cover one or two quick topics. As you may recall from
the discussion of the formal definition of a match pattern, you can
create patterns that match elements by ID or by key in addition to
using step patterns.
In addition to making patterns out of step patterns that specify
an axis, node test, and predicate, you can also use the id()
pattern to match elements that have a specific ID value. To use
this pattern, you must give elements an ID attribute, and you must
declare that attribute of type ID, as you can do in a DTD or
schema. The following example rule adds the text of all elements
that have the ID "favorite":
<xsl:template match = "id('favorite')">
<H3><xsl:value-of
select="."/></H3>
</xsl:template>
Here's what a DTD for planets.xml might look like that declares
an ID and sets it to "favorite":
Some XSLT processors don't match by ID because they don't read
DDS or XML schema. (This is one of the issues that XSLT 2.0 is
supposed to address how to make ID information available.) However,
there's another option: you can match by key.
In addition to making it easier to work with IDs, W3C is even
considering adding support for IDREFs in XSLT 2.0. In particular,
given an ID, the XSLT processor could provide you with a list of
all elements that have an IDREF or IDREFS attribute that refers to
that ID. (Note that currently, however, you can do the same thing
using <xsl:key> and the "key()" pattern.)
Keys give you an easy way to identify elements, and you can
match specific keys with the pattern "key()". Chapter 9 discusses
how to use keys in detail, but I'll include a quick example
here.
You use the <xsl:key> element to create a key. This
element is a top-level element, so it appears outside templates and
as a child element of <xsl:stylesheet>. In the following
example, I use a key to match planets whose COLOR attribute is set
to "BLUE", which means Earth:
Using the Or operator, |, you can match to a number of possible
patterns, which is very useful when your documents get a little
more involved. In the following example, I want to display
<NAME> and <MASS> elements in bold, which I do with the
HTML <B> tag. To match either <NAME> or
<MASS> elements, I'll use the Or operator in a new rule:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/PLANETS">
<HTML>
<HEAD>
.
.
.
</BODY>
</HTML>
</xsl:template>
<xsl:template match="PLANET">
<TR>
<TD><xsl:apply-templates select="NAME"/></TD>
<TD><xsl:apply-templates select="MASS"/></TD>
<TD><xsl:apply-templates
select="RADIUS"/></TD>
<TD><xsl:apply-templates
select="DAY"/></TD>
</TR>
</xsl:template>
<xsl:template match="NAME | MASS">
<B>
<xsl:apply-templates/>
</B>
</xsl:template>
<xsl:template match="RADIUS">
<xsl:value-of
select="."/>
<xsl:text>
</xsl:text>
<xsl:value-of
select="@UNITS"/>
</xsl:template>
<xsl:template match="DAY">
<xsl:value-of
select="."/>
<xsl:text>
</xsl:text>
<xsl:value-of
select="@UNITS"/>
</xsl:template>
</xsl:stylesheet>
Here are the results; note that the name and mass values are
both enclosed in <B> elements:
<HTML>
<HEAD>
<TITLE>
The Planets Table
</TITLE>
</HEAD>
<BODY>
.
.
.
<TR>
<TD><B>Mercury</B></TD>
<TD><B>.0553</B></TD>
<TD>1516 miles</TD>
<TD>58.65 days</TD>
</TR>
<TR>
<TD><B>Venus</B></TD>
<TD><B>.815</B></TD>
<TD>3716 miles</TD>
<TD>116.75 days</TD>
</TR>
<TR>
<TD><B>Earth</B></TD>
<TD><B>1</B></TD>
<TD>2107 miles</TD>
<TD>1
days</TD>
</TR>
</TABLE>
</BODY>
</HTML>
You can use any valid pattern with the | operator, such as
expressions like "PLANET | PLANET//NAME", and you can use multiple
| operators, like "NAME | MASS | DAY", and so on.