<xsl:sort>
This seems to be a good point to introduce <xsl:sort>.
This element can be used inside <xsl:apply-templates>
or <xsl:for-each>
to alter the order in which nodes are processed. In most cases, when either of
these statements selects these nodes, they will be processed in document order
that is, the order in which they were encountered when processing the
document from top to bottom. This order can be altered using <xsl:sort>.
At the start of this chapter, we introduced a book catalog
with just two books in it and a simplified structure. Now is a good time to use
the full catalog, catalog.xml, which is available in the code download. Here
are
the first two books
from this version:
<?xml
version="1.0" encoding="utf-8"
standalone="yes"?>
<!--======= The
Wrox Press Book Catalog Application =======-->
<Catalog>
<Book
id="1861003323">
<Title>Professional
VB6
XML</Title>
<Authors>
<Author>James
Britt</Author>
<Author>Teun
Duynstee</Author>
</Authors>
<Publisher>Wrox
Press,
Ltd.</Publisher>
<PubDate>March
2000</PubDate>
<Abstract>Shows
the VB community how
to take advantage of XML
technology and the
available
implementations</Abstract>
<Pages>725</Pages>
<ISBN>1-861003-32-3</ISBN>
<RecSubjCategories>
<Category>Programming</Category>
<Category>Visual
Basic</Category>
<Category>XML</Category>
</RecSubjCategories>
<Price>$49.99</Price>
<CoverImage>3323.gif</CoverImage>
</Book>
<Book
id="1861003129">
<Title>XSLT
Programmer's
Reference</Title>
<Authors>
<Author>Michael
Kay</Author>
</Authors>
<Publisher>Wrox
Press,
Ltd.</Publisher>
<PubDate>April
2000</PubDate>
<Abstract>Learn
how to use the XSLT
language to develop
web
applications</Abstract>
<Pages>800</Pages>
<ISBN>1-861003-12-9</ISBN>
<RecSubjCategories>
<Category>Internet</Category>
<Category>XML</Category>
<Category>XSL</Category>
</RecSubjCategories>
<Price>$34.99</Price>
<CoverImage>3129.gif</CoverImage>
</Book>
...
</Catalog>
We will be using this quite a bit later in the chapter, but
for now we will use it to see the effect of <xsl:sort>.
Firstly, let's get
a list of the book titles using ShowTitles.xsl:
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template
match="/"><xsl:apply-templates/></xsl:template>
<xsl:template
match="Catalog">
<xsl:for-each
select="Book">
<DIV><xsl:value-of
select="Title"/></DIV>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
When applied to catalog.xml,
this gives the result:
If you look through the catalog, you will see that this is
the order in which the books are listed. Now let's add an <xsl:sort>
statement to the stylesheet (and re-save it as CatalogSort.xsl):
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template
match="/"><xsl:apply-templates/></xsl:template>
<xsl:template
match="Catalog">
<xsl:for-each select="Book">
<xsl:sort select="@id"/>
<DIV><xsl:value-of
select="Title"/></DIV>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
The books are now listed in the order of their id
attributes. These are their ISBN numbers
without the hyphens. We could have just used the ISBN numbers, but the sort
would then get confused if the style of hyphenation was not consistent. This is
the result of using the stylesheet above on catalog.xml:
Inspection of the catalog will show that the titles are now
listed in ISBN order.
There are various optional attributes of <xsl:sort>
that control the sorting. We have already used select.
Had we omitted this, the string value of the current node would have been used,
rather than the id attribute. In this case, since the node has element
content, this is not really a safe option (the string value would be the
concatenation of the string values of the descendant nodes), but had we instead
created a template for the <Title>
element, we could have
sorted on this by using the default value for the select
attribute. The stylesheet might then
have looked like this (CatalogSort2.xsl):
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:for-each select="Catalog/Book/Title">
<xsl:sort/>
<DIV><xsl:value-of select="."/></DIV>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
And the result on transforming catalog.xml
would have looked like this:
Because it is now applied to the <Title>
elements, the line:
causes the titles to be output in alphabetical order.
Using the data-type
attribute of <xsl:sort>,
we can specify whether the data on which we are sorting is text
(the default) or number. We sorted our ISBN values as text, which works
in this case of fixed length strings, but in other cases, there would be a
difference. The sequence of numbers from 1 to 20 sorted as text would be:
1,10,11,12,13,14,15,16,17,18,19,2,20,3,4,5,6,7,8,9
The order
attribute has two possible
values: ascending
(the default) and descending, which
are self-explanatory.
The lang
attribute controls
language-specific sorting orders. For example, in German, it is conventional to
include the letter äafter the lettera,
while in Swedish it would come afterz.
The possible values are the same as those of the xml:lang
attribute and are specified inIETF RFC 1766 (http://www.cis.ohio-state.edu/htbin/rfc/rfc1766.html),
"Tags for the Identification of Languages".
When sorting textual
information, we can choose whether upper-case or lower-case values come first
using the case-order attribute. This has values of upper-first and lower-first. If the former is used, when values start with the
same letter, values starting with upper-case letters will occur in the output
before any starting with lower-case letters. The default for this is
language-dependent, and not specified in the recommendation. With no language
specified, both XT and SAXON ignore case when sorting.
Sorting can be achieved on multiple criteria by using <xsl:sort>
elements sequentially. In this case, sorting will occur first using the
criteria defined by the first element, then sorted within these results using
the later elements in order. If, for some strange reason, we wanted to sort all
the speeches of Hamlet first in order of speaker, then by number of lines in
the speech, then in alphabetical order of the text, we could use the following
stylesheet, HamletSort.xsl:
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:apply-templates select="//SCENE"/>
</xsl:template>
<xsl:template
match="SCENE"><xsl:for-each select="SPEECH">
<xsl:sort select="SPEAKER"/>
<xsl:sort select="count(LINE)"/>
<xsl:sort select="LINE"/>
<DIV><xsl:value-of select="SPEAKER"/></DIV>
<xsl:for-each select="LINE">
<DIV><xsl:value-of select="."/></DIV>
</xsl:for-each>
</xsl:for-each></xsl:template>
</xsl:stylesheet>
Using this just on our extract of the first scene (HamletExtract.xml),
all Bernardo's speeches come before Francisco's, then for each speaker, all the
one line speeches come before the two line speeches and within these criteria,
the speeches are listed in alphabetical order. Notice that the apostrophe comes
before any letters, as it does in English-language dictionaries: