Read pages 240-245 in Learning XML
Here is another example, in addition to the one given in the book. Let's take our movie database and generate an HTML page with an index at beginning that links to the long description later on in the page. Here's what the result will look like, and here's some of the HTML:
<h2>Movie List</h2> <ul> <li><a href="#movie_1">The Maltese Falcon (1931)</a></li> <li><a href="#movie_2">The Maltese Falcon (1941)</a></li> <li><a href="#movie_3">Trouble In Paradise (1932)</a></li> </ul> <hr> <h3> <a name="movie_1">The Maltese Falcon (1931)</a> </h3> <!-- etc. -->
Ths means that we have to process each <movie>
element twice; once to put it in the index list, and once to insert its
long description. A first attempt at a template for the
<movie-list>
element might look like this:
<xsl:template match="movie-list"> <html> <head> <title>Movie List</title> </head> <body style="font-size:12pt;"> <h2>Movie List</h2> <ul> <xsl:apply-templates select="movie"/> </ul> <hr /> <xsl:apply-templates select="movie"/> </body> </html> </xsl:template> <xsl:template match="movie"> <!-- output for list here --> </xsl:template> <xsl:template match="movie"> <!-- output for description here --> </xsl:template>
But this won't work. Since both the <xsl:template
match="movie">
templates are equally specific (see the rules
on pages 214-215 of Learning XML), Xalan will use the last one
in the file, and use it for both
<xsl:apply-templates>
.
What we really want to say is that there are two different
templates to handle the <movie>
element; one when
we're in list-building mode, and one when we're in description-output
mode. Here's our revised outline:
<xsl:template match="movie-list"> <html> <head> <title>Movie List</title> </head> <body style="font-size:12pt;"> <h2>Movie List</h2> <ul> <xsl:apply-templates select="movie" mode="make_list"/> </ul> <hr /> <xsl:apply-templates select="movie" mode="long_desc"/> </body> </html> </xsl:template> <xsl:template match="movie" mode="make_list"> <!-- output for list here --> </xsl:template> <xsl:template match="movie" mode="long_desc"> <!-- output for description here --> </xsl:template>
Here's the entire stylesheet, with line numbers for reference.
1 <xsl:stylesheet version="1.0" 2 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 3 4 <xsl:output method="html"/> 5 6 <xsl:template match="movie-list"> 7 <html> 8 <head> 9 <title>Movie List</title> 10 </head> 11 <body style="font-size:12pt;"> 12 <h2>Movie List</h2> 13 <ul> 14 <xsl:apply-templates select="movie" mode="make_list"/> 15 </ul> 16 17 <hr /> 18 19 <xsl:apply-templates select="movie" mode="long_desc"/> 20 </body> 21 </html> 22 </xsl:template> 23 24 <xsl:template match="movie" mode="make_list"> 25 <li><a href="#movie_{position()}"> 26 <xsl:value-of select="heading/title"/> 27 (<xsl:value-of select=".//year"/>)</a></li> 28 </xsl:template> 29 30 <xsl:template match="movie" mode="long_desc"> 31 <h3> 32 <a name="movie_{position()}"><xsl:value-of select="heading/title"/> 33 (<xsl:value-of select=".//year"/>)</a></h3> 34 <div style="border-bottom: 1px dashed black;"> 35 <xsl:apply-templates select="synopsis"/> 36 <xsl:apply-templates select="credits/actors"/> 37 </div> 38 </xsl:template> 39 40 41 <xsl:template match="synopsis"> 42 <div style="margin-left: 2em; margin-right: 2em;"> 43 <xsl:for-each select="para"> 44 <p><xsl:apply-templates/></p> 45 </xsl:for-each> 46 </div> 47 </xsl:template> 48 49 50 <xsl:template match="credits/actors"> 51 <h4>The Cast:</h4> 52 <ul> 53 <xsl:apply-templates select="actor"/> 54 </ul> 55 </xsl:template> 56 57 <xsl:template match="actor[@special='yes']"> 58 <li><span style="color:green;"> 59 <xsl:value-of select="."/></span></li> 60 </xsl:template> 61 62 <xsl:template match="actor"> 63 <li><xsl:value-of select="."/></li> 64 </xsl:template> 65 66 </xsl:stylesheet>
We generate the link name by appending the movie's position in
the list to the word movie
. Notice that we need the curly
braces to force evaluation of an expression. That is much shorter than
the equivalent:
<a> <xsl:attribute name="href">#movie_<xsl:value-of select="position()"/></xsl:attribute>
Laziness reigns supreme. Rather than use
heading/technical-details/year
as a selector, I decided
to use .//year
to find the <year>
descendant of the current node.
You might think that this won't work either; how can I
have two templates that match <actor>
? The reason
it puts special actors in green and non-special actors in black
is rule 4 on page 215: “A pattern with a successful test
expression in square brackets overrides a pattern with no test
expression that is otherwise identical.”
By the way, my initial approach to this was 100% wrong. I wanted to
use an <xsl:if>
twice, once to put in a preceding
<span>
and once to put in a closing
</span>
. This is wrong, because the template
is not well-formed!
<xsl:template match="actor"> <li> <xsl:if test="@special='yes'"> <span style="color: green;"> </xsl:if> <xsl:value-of select="."/> <xsl:if test="@special='yes'"> </span> </xsl:if> </li> </xsl:template>