Example 2-6. A document to transform
<book>
<title>The Blathering Brains</title>
<chapter>
<title>At the Bazaar</title>
<para>What a fantastic day it was. The crates were stacked
high with imported goods: dates, bananas, dried meats,
fine silks, and more things than I could imagine. As I
walked around, savoring the fragrances of cinnamon and
cardamom, I almost didn't notice a small booth with a
little man selling brains.</para>
<para>Brains! Yes, human brains, still quite moist and squishy,
swimming in big glass jars full of some greenish
fluid.</para>
<para>"Would you like a brain, sir?" he asked. "Very reasonable
prices. Here is Enrico Fermi's brain for only two
dracmas. Or, perhaps, you would prefer Aristotle? Or the
great emperor Akhnaten?"</para>
<para>I recoiled in horror...</para>
</chapter>
</book>
Let's walk through the transformation.
-
The first element is <book>. The best
matching rule is the first one, because it explicitly matches
"book." The template says to output
tags like <html>,
<head>, and
<title>. Note that these tags are treated as
data markup because they don't have the
xsl: namespace prefix.
-
When the processor gets to the XSLT instruction
<xsl:value-of select="title"/>, it has to
find a <title> element that is a child of
the current element, <book>. Then it must
obtain the value of that element, which is
simply all the text contained within it. This text is output inside a
<title> element as the template directs.
-
The processor continues in this way until it gets to the
<xsl:call-template
name="toc"/> instruction. If you look at the
bottom of the stylesheet, you'll find a template
rule that begins with <xsl:template
name="toc">. This template rule is a named
template and acts like a function call. It assembles a
table of contents and returns the text to the calling rule for
output.
-
Inside the named template is an element called <xsl:if
test="count(chapter)>0">. This element is a
conditional statement whose test is whether more than one
<chapter> is inside the current element
(still <book>). The test passes, and
processing continues inside the element.
-
The <xsl:for-each select="chapter">
instruction causes the processor to visit each
<chapter> child element and temporarily make
it the current element while in the body of the
<xsl:for-each> element. This step is
analogous to a foreach( ) loop in Perl. The
<xsl:value-of select="position( )"/>
statement derives the numerical position of each
<chapter> and outputs it so that the result
document reads "Chapter 1,"
"Chapter 2," and so on.
-
The named template "toc" returns
its text to the calling rule and execution continues. Next, the
processor receives an <xsl:apply-templates
select="chapter"/> directive. An output of
<xsl:apply-templates> without any attributes
means that the processor should then process each of the current
element's children, making it the current element.
However, since a select="chapter" attribute is
present, only children who are of type
<chapter> should be processed. After all
descendants have been processed and this instruction returns its
text, it will be output and the rest of the rule will be followed
until the end.
-
Moving on to the first <chapter> element,
the processor locates a suitable rule and sees only an
<xsl:apply-tempaltes/> rule. The rest of the
processing is pretty easy, as the rules for the remaining elements,
<title> and <para>,
are straightforward.
XSLT is a rich language for handling transformations, but often
leaves something to be desired. It can be slow on large documents,
since it has to build an internal representation of the whole
document before it can do any processing. Its syntax, while a
remarkable achievement for XML, is not as expressive and easy to use
as Perl. We will explore numerous Perl solutions to some problems
that XSL could also solve. You'll have to decide
whether you prefer XSLT's simplicity or
Perl's power.