XSLT Functions
After that little aside to understand a couple of important
areas of XSLT, we can get back to what
XSLT can do for us. Now that we have covered the most important elements, we
can cover the
most important functions.
In Chapter 2, XPath core functions were introduced. In
addition, XSLT has some functions of its own. While the core XPath functions
are available to XSLT, the XSLT defined functions are not available to XPath
when it is used beyond the confines of XSLT. The functions that we take a
closer look at now are actually core XPath functions, but exist in the XSLT
namespace.
The names of these functions usually give away what they do.
Those we will cover here are:
q
position()
q
last()
q
name()
q
count()
We will also look at accessing nodes in a node-set by index.
position() and last()
In many cases, we will want to select nodes based on their
position in a node-set. This is what we did in ListCharacters.xsl
when we made the list of characters in Hamlet, to ensure that the last
character did not have a comma after his name:
<xsl:if test="position()!=last()">, </xsl:if>
As well as checking against last(),
we can check against any index into the node-set. It is this that means we do
not need a first()
function to match the last() function. Instead, we just look for a position()
of 1.
To apply a template only if there is only one matching elem
node, we would use:
<xsl:template
match="elem[position()='1' and position()=last()]">
Note that the first matching node has an index of 1, and not
0.
As well as using the position within a test, we can use the
value of the position()
function to number our nodes. Earlier, we looked at <xsl:number>,
which provides us with a number relating to a node's position in the source
tree. In contrast, position()
provides a number relating to the position in a node-set, which might be after
a sort operation. It can therefore be used to provide a number relating to a
node's position in the result tree.
We can very easily demonstrate this difference by using both
methods to number the books in our catalog. We will produce a table of book
titles with the results of numbering using <xsl:number>
and position().
This is our stylesheet, position.xsl:
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="http://www.w3.org/TR/REC-html40">
<xsl:template
match="/">
<TABLE
BORDER="1">
<TR>
<TD>Title</TD>
<TD>position()</TD>
<TD>xsl:number</TD>
</TR>
<xsl:apply-templates
select="Catalog/Book"/>
</TABLE>
</xsl:template>
<xsl:template
match="Book">
<TR>
<TD><xsl:value-of
select="Title"/></TD>
<TD
align="center"><xsl:value-of
select="position()"/></TD>
<TD
align="center"><xsl:number/></TD>
</TR>
</xsl:template>
</xsl:stylesheet>
This is extremely simple.
The template for the document root creates the table and applies the template
for the <Book> elements. Each time the <Book> template executes, it adds a row to the table and
puts in the book title with the result of applying <xsl:number> and position() to the current
<Book> element.
The result of applying this stylesheet to catalog.xml
looks like this:
In this case, the results are the same since the node-set
produced by the select attribute in the <xsl:apply-templates>
element will be in document order. However, we can sort the catalog
alphabetically by changing the template for the document root (number.xsl):
<xsl:template match="/">
<TABLE BORDER="1">
<TR>
<TD>Title</TD>
<TD>position()</TD>
<TD>xsl:number</TD>
</TR>
<xsl:apply-templates select="Catalog/Book">
<xsl:sort select="Title"/>
</xsl:apply-templates>
</TABLE>
</xsl:template>
Now the results will be somewhat different:
The position()
function has produced
numbering in the order of the node-set provided to the <Book>
template (which is also the order in the result tree), while the <xsl:number>
element has kept the numbering as it was in the source tree (so each title is
numbered as it was in the previous example).
name()
The name()
function merely returns the
name of the node. If applied to a node-set
rather than a node, the optional node
parameter can identify the node
required. Where there is more than one match, the function returns the first.
There are often occasions when you want to display a node
name. For example, I often use XSLT stylesheets to document XML Schemas. In one
of these, I want to display the facets of an element. Don't worry if you are
not familiar with XML Schema, the important thing is that each facet is defined
by its element name and the value of its value
attribute. An extract of the
stylesheet I use is:
<table>
<xsl:for-each
select="*">
<tr>
<td><xsl:value-of
select="name()"/>:</td>
<td><xsl:value-of
select="@value"/></td>
</tr>
</xsl:for-each>
</table>
We therefore list each facet name and value
attribute value in two columns in a table.
count()
The final function we will consider here is the count()
function, which returns the number of nodes in a node-set.
In the book catalog, catalog.xml,
we could provide a
template to list the titles of all the books with more than one author:
<xsl:template
match="Book[count(//Authors)>1]">
<xsl:value-of
select="Title"/>
</xsl:template>
Or, we might want to sort the books by the number of authors
(CountAuthors.xsl):
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template
match="/">
<xsl:apply-templates
select="Catalog"/>
</xsl:template>
<xsl:template
match="Catalog">
<xsl:for-each
select="Book">
<xsl:sort
select="count(Authors/Author)" data-type="number"/>
<P>
<xsl:value-of
select="Title"/>
has
<xsl:value-of
select="count(Authors/Author)"/> author(s)
</P>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
The result will be a list of books sorted in increasing
number of authors order, and saying how many authors there are in each.
Summary
In this chapter, we have learnt the basics of XSLT through
discussion and examples. In particular, we have seen:
q
How XSLT is a declarative language with procedural
elements, matching XPath expressions to apply templates to nodes in the source
tree, while including control flow elements to provide the procedural aspects.
q
The push and pull models, and where each is most
suitable. We have also seen that most stylesheets will use some combination of
these.
q
A number of the elements used in XSLT, and how we can
write useful stylesheets with just this subset of the language.
q
A few of the functions built into the language, and how
we can use these to give more control over our processing.
q
How the built-in template rules ensure that all nodes
are processed (unless we over ride them) and how XSLT knows which rule to apply
when there is a conflict.
Before we move on to more advanced XSLT processing, why not
try a few stylesheets of your own, perhaps gathering more statistics about
Hamlet or displaying the book catalog in different ways?