This chapter is a complete reference to all the elements defined in the XSLT specification.
<xsl:apply-imports> | Allows you to apply any overridden templates to the current node. It is comparable to the super() method in Java. |
Instruction
None.
None.
None. <xsl:apply-imports> is an empty element.
<xsl:apply-imports> appears inside a template.
XSLT section 5.6, Overriding Template Rules.
Here is a short XML file we'll use to illustrate <xsl:apply-imports>:
<?xml version="1.0"?> <test> <p>This is a test XML document used by several of our sample stylesheets.</p> <question> <text>When completed, the Eiffel Tower was the tallest building in the world.</text> <true correct="yes">You're correct! The Eiffel Tower was the world's tallest building until 1930.</true> <false>No, the Eiffel Tower was the world's tallest building for over 30 years.</false> </question> <question> <text>New York's Empire State Building knocked the Eiffel Tower from its pedestal.</text> <true>No, that's not correct.</true> <false correct="yes">Correct! New York's Chrysler Building, completed in 1930, became the world's tallest.</false> </question> </test>
Here's the stylesheet we'll import:
<?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="html"/> <xsl:template match="/"> <html> <body> <xsl:for-each select="//text|//true|//false"> <p> <xsl:apply-templates select="."/> </p> </xsl:for-each> </body> </html> </xsl:template> <xsl:template match="text"> <xsl:text>True or False: </xsl:text><xsl:value-of select="."/> </xsl:template> <xsl:template match="true|false"> <b><xsl:value-of select="name()"/>:</b> <br/> <xsl:value-of select="."/> </xsl:template> </xsl:stylesheet>
This template provides basic formatting for the <true> and <false> elements, as shown in Figure A-1.
We'll illustrate <xsl:apply-imports> with this stylesheet, which imports the other stylesheet:
<?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:import href="imported.xsl"/> <xsl:output method="html"/> <xsl:template match="/"> <html> <head> <title>A Brief Test</title> <style> <xsl:comment> p.question {font-size: 125%; font-weight: bold} p.right {color: green} p.wrong {color: red} </xsl:comment> </style> </head> <body> <h1>A Brief Test</h1> <xsl:for-each select="//question"> <table border="1"> <xsl:apply-templates select="text"/> <xsl:apply-templates select="true|false"/> </table> <br/> </xsl:for-each> </body> </html> </xsl:template> <xsl:template match="text"> <tr bgcolor="lightslategray"> <td> <p class="question"> <xsl:apply-imports/> </p> </td> </tr> </xsl:template> <xsl:template match="true|false"> <tr> <td> <xsl:choose> <xsl:when test="@correct='yes'"> <p class="right"> <xsl:apply-imports/> </p> </xsl:when> <xsl:otherwise> <p class="wrong"> <xsl:apply-imports/> </p> </xsl:otherwise> </xsl:choose> </td> </tr> </xsl:template> </xsl:stylesheet>
Using <xsl:apply-imports> allows us to augment the behavior of the imported templates. Our new stylesheet produces this document:
<html> <head> <META http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>A Brief Test</title> <style> <!-- p.question {font-size: 125%; font-weight: bold} p.right {color: green} p.wrong {color: red} --> </style> </head> <body> <h1>A Brief Test</h1> <table border="1"> <tr bgcolor="lightslategray"> <td> <p class="question">True or False: When completed, the Eiffel Tower was the tallest building in the world.</p> </td> </tr> <tr> <td> <p class="right"> <b>true:</b> <br>You're correct! The Eiffel Tower was the world's tallest building until 1930.</p> </td> </tr> <tr> <td> <p class="wrong"> <b>false:</b> <br>No, the Eiffel Tower was the world's tallest building for over 30 years.</p> </td> </tr> </table> <br> <table border="1"> <tr bgcolor="lightslategray"> <td> <p class="question">True or False: New York's Empire State Building knocked the Eiffel Tower from its pedestal.</p> </td> </tr> <tr> <td> <p class="wrong"> <b>true:</b> <br>No, that's not correct.</p> </td> </tr> <tr> <td> <p class="right"> <b>false:</b> <br>Correct! New York's Chrysler Building, completed in 1930, became the world's tallest.</p> </td> </tr> </table> <br> </body> </html>
When rendered, this stylesheet looks like Figure A-2.
<xsl:apply-templates> | Instructs the XSLT processor to apply the appropriate templates to a node-set. |
Instruction
None.
The <xsl:apply-templates> element can contain any number of <xsl:sort> and <xsl:with-param> elements. In most cases, <xsl:apply-templates> is empty.
<xsl:apply-templates> appears inside a template.
XSLT section 5.4, Applying Template Rules.
In our case study (see Chapter 9, "Case Study: The Toot-O-Matic"), we needed to create several different outputs from the same data. We addressed this need with the mode attribute of the <xsl:apply-templates> element. Here's the main template (match="/"):
<xsl:template match="/"> <xsl:apply-templates select="tutorial" mode="build-main-index"/> <redirect:write select="concat($curDir, $fileSep, 'index.html')"> <xsl:apply-templates select="tutorial" mode="build-main-index"/> </redirect:write> <xsl:apply-templates select="tutorial" mode="build-section-indexes"/> <xsl:apply-templates select="tutorial" mode="build-individual-panels"/> <xsl:apply-templates select="tutorial" mode="generate-graphics"/> <xsl:apply-templates select="tutorial" mode="generate-pdf-file"> <xsl:with-param name="page-size" select="'ltr'"/> </xsl:apply-templates> <xsl:apply-templates select="tutorial" mode="generate-pdf-file"> <xsl:with-param name="page-size" select="'a4'"/> </xsl:apply-templates> <xsl:apply-templates select="tutorial" mode="generate-zip-file"/> </xsl:template>
Notice that this example selects the <tutorial> element eight times, but applies templates with a different mode (or different parameters for the same mode) each time.
<xsl:attribute> | Allows you to create an attribute in the output document. The advantage of <xsl:attribute> is that it allows you to build the attribute's value from parts of the input document, hardcoded text, values returned by functions, and any other value you can access from your stylesheet. |
Instruction
An XSLT template. In other words, you can build the contents of an attribute with <xsl:choose> elements, <xsl:text>, and <xsl:value-of> elements.
<xsl:attribute> appears inside a template.
XSLT section 7.1.3, Creating Attributes with xsl:attribute.
For this example, we want to create an HTML table from the following XML document:
<?xml version="1.0"?> <list xml:lang="en"> <title>Albums I've bought recently:</title> <listitem>The Sacred Art of Dub</listitem> <listitem>Only the Poor Man Feel It</listitem> <listitem>Excitable Boy</listitem> <listitem xml:lang="sw">Aki Special</listitem> <listitem xml:lang="en-gb">Combat Rock</listitem> <listitem xml:lang="zu">Talking Timbuktu</listitem> <listitem xml:lang="jz">The Birth of the Cool</listitem> </list>
We'll create a table that has each <listitem> in a separate row in the right column of the table, and a single cell with rowspan equal to the number of <listitem> elements in the XML document on the left. Clearly we can't hardcode a value for the rowspan attribute because the number of <listitem>s can change. This stylesheet uses <xsl:attribute> to do what we want:
<?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="html"/> <xsl:template match="/"> <html> <head> <title><xsl:value-of select="list/title"/></title> </head> <body> <xsl:apply-templates select="list"/> </body> </html> </xsl:template> <xsl:template match="list"> <table border="1" width="75%"> <tr> <td bgcolor="lightslategray" width="100" align="right"> <xsl:attribute name="rowspan"> <xsl:value-of select="count(listitem)"/> </xsl:attribute> <p style="font-size: 125%"> <xsl:value-of select="title"/> </p> </td> <td> <xsl:value-of select="listitem[1]"/> </td> </tr> <xsl:for-each select="listitem"> <xsl:if test="position() > 1"> <tr> <td> <xsl:value-of select="."/> </td> </tr> </xsl:if> </xsl:for-each> </table> </xsl:template> </xsl:stylesheet>
Here is the generated HTML document:
<html> <head> <META http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Albums I've bought recently:</title> </head> <body> <table width="75%" border="1"> <tr> <td align="right" width="100" rowspan="7" bgcolor="lightslategray"> <p style="font-size: 125%">Albums I've bought recently:</p> </td><td>The Sacred Art of Dub</td> </tr> <tr> <td>Only the Poor Man Feel It</td> </tr> <tr> <td>Excitable Boy</td> </tr> <tr> <td>Aki Special</td> </tr> <tr> <td>Combat Rock</td> </tr> <tr> <td>Talking Timbuktu</td> </tr> <tr> <td>The Birth of the Cool</td> </tr> </table> </body> </html>
Notice that the <td> element had several attributes hardcoded on it; those attributes are combined with the attribute we created with <xsl:attribute>. You can have as many <xsl:attribute> elements as you want, but they must appear together as the first thing inside the element to which you add attributes. Figure A-3 shows how our generated HTML document looks.
Be aware that in this instance, we could have used an attribute-value template. You could generate the value of the rowspan attribute like this:
<td bgcolor="lightslategray" rowspan="{count(listitem)}" width="100" align="right">
The expression in curly braces ({}) is evaluated and replaced with whatever its value happens to be. In this case, count(listitem) returns the number 7, which becomes the value of the rowspan attribute.
<xsl:attribute-set> | Allows you to define a group of attributes for the output document. You can then reference the entire attribute set with its name, rather than create all attributes individually. |
Top-level element
One or more <xsl:attribute> elements.
<xsl:stylesheet>. <xsl:attribute-set> is a top-level element and can only appear as a child of <xsl:stylesheet>.
XSLT section 7.1.4, Named Attribute Sets.
For this example, we'll create a stylesheet that defines attribute sets for regular text, emphasized text, and large text. Just for variety's sake, we'll use the Extensible Stylesheet Language Formatting Objects (XSL-FO) specification to convert our XML document into a PDF file. Here's our stylesheet:
<?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format"> <xsl:output method="html"/> <xsl:attribute-set name="regular-text"> <xsl:attribute name="font-size">12pt</xsl:attribute> <xsl:attribute name="font-family">sans-serif</xsl:attribute> </xsl:attribute-set> <xsl:attribute-set name="emphasized-text" use-attribute-sets="regular-text"> <xsl:attribute name="font-style">italic</xsl:attribute> </xsl:attribute-set> <xsl:attribute-set name="large-text" use-attribute-sets="regular-text"> <xsl:attribute name="font-size">18pt</xsl:attribute> <xsl:attribute name="font-weight">bold</xsl:attribute> <xsl:attribute name="space-after.optimum">21pt</xsl:attribute> </xsl:attribute-set> <xsl:template match="/"> <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format"> <fo:layout-master-set> <fo:simple-page-master margin-right="75pt" margin-left="75pt" page-height="11in" page-width="8.5in" margin-bottom="25pt" margin-top="25pt" master-name="main"> <fo:region-before extent="25pt"/> <fo:region-body margin-top="50pt" margin-bottom="50pt"/> <fo:region-after extent="25pt"/> </fo:simple-page-master> <fo:page-sequence-master master-name="standard"> <fo:repeatable-page-master-alternatives> <fo:conditional-page-master-reference master-name="main" odd-or-even="any"/> </fo:repeatable-page-master-alternatives> </fo:page-sequence-master> </fo:layout-master-set> <fo:page-sequence master-name="standard"> <fo:flow flow-name="xsl-region-body"> <xsl:apply-templates select="list"/> </fo:flow> </fo:page-sequence> </fo:root> </xsl:template> <xsl:template match="list"> <fo:block xsl:use-attribute-sets="large-text"> <xsl:value-of select="title"/> </fo:block> <fo:list-block provisional-distance-between-starts="0.4cm" provisional-label-separation="0.15cm"> <xsl:for-each select="listitem"> <fo:list-item start-indent="0.5cm" space-after.optimum="17pt"> <fo:list-item-label> <fo:block xsl:use-attribute-sets="regular-text">*</fo:block> </fo:list-item-label> <fo:list-item-body> <fo:block xsl:use-attribute-sets="emphasized-text"> <xsl:value-of select="."/> </fo:block> </fo:list-item-body> </fo:list-item> </xsl:for-each> </fo:list-block> </xsl:template> </xsl:stylesheet>
Notice that both the emphasized-text and large-text attribute sets use the regular-text attribute set as a base. In the case of large-text, the font-size attribute defined in the large-text attribute set overrides the font-size attribute included from the regular-text attribute set. We'll apply our stylesheet to the following XML document:
<?xml version="1.0"?> <list> <title>A few of my favorite albums</title> <listitem>A Love Supreme</listitem> <listitem>Beat Crazy</listitem> <listitem>Here Come the Warm Jets</listitem> <listitem>Kind of Blue</listitem> <listitem>London Calling</listitem> <listitem>Remain in Light</listitem> <listitem>The Joshua Tree</listitem> <listitem>The Indestructible Beat of Soweto</listitem> </list>
The stylesheet generates this messy-looking file of formatting objects, which describe how the text of our XML source document should be rendered:
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format"> <fo:layout-master-set> <fo:simple-page-master master-name="main" margin-top="25pt" margin-bottom="25pt" page-width="8.5in" page-height="11in" margin-left="75pt" margin-right="75pt"> <fo:region-before extent="25pt"/> <fo:region-body margin-bottom="50pt" margin-top="50pt"/> <fo:region-after extent="25pt"/> </fo:simple-page-master> <fo:page-sequence-master master-name="standard"> <fo:repeatable-page-master-alternatives> <fo:conditional-page-master-reference odd-or-even="any" master-name="main"/> </fo:repeatable-page-master-alternatives> </fo:page-sequence-master> </fo:layout-master-set> <fo:page-sequence master-name="standard"> <fo:flow flow-name="xsl-region-body"> <fo:block font-size="18pt" font-family="sans-serif" font-weight="bold" space-after.optimum="21pt">A few of my favorite albums</fo:block> <fo:list-block provisional-label-separation="0.15cm" provisional-distance-between-starts="0.4cm"> <fo:list-item space-after.optimum="17pt" start-indent="0.5cm"> <fo:list-item-label> <fo:block font-size="12pt" font-family="sans-serif">*</fo:block> </fo:list-item-label> <fo:list-item-body> <fo:block font-size="12pt" font-family="sans-serif" font-style="italic">A Love Supreme</fo:block> </fo:list-item-body> </fo:list-item> <fo:list-item space-after.optimum="17pt" start-indent="0.5cm"> <fo:list-item-label> <fo:block font-size="12pt" font-family="sans-serif">*</fo:block> </fo:list-item-label> <fo:list-item-body> <fo:block font-size="12pt" font-family="sans-serif" font-style="italic">Beat Crazy</fo:block> </fo:list-item-body> </fo:list-item> <fo:list-item space-after.optimum="17pt" start-indent="0.5cm"> <fo:list-item-label> <fo:block font-size="12pt" font-family="sans-serif">*</fo:block> </fo:list-item-label> <fo:list-item-body> <fo:block font-size="12pt" font-family="sans-serif" font-style="italic">Here Come the Warm Jets</fo:block> </fo:list-item-body> </fo:list-item> <fo:list-item space-after.optimum="17pt" start-indent="0.5cm"> <fo:list-item-label> <fo:block font-size="12pt" font-family="sans-serif">*</fo:block> </fo:list-item-label> <fo:list-item-body> <fo:block font-size="12pt" font-family="sans-serif" font-style="italic">Kind of Blue</fo:block> </fo:list-item-body> </fo:list-item> <fo:list-item space-after.optimum="17pt" start-indent="0.5cm"> <fo:list-item-label> <fo:block font-size="12pt" font-family="sans-serif">*</fo:block> </fo:list-item-label> <fo:list-item-body> <fo:block font-size="12pt" font-family="sans-serif" font-style="italic">London Calling</fo:block> </fo:list-item-body> </fo:list-item> <fo:list-item space-after.optimum="17pt" start-indent="0.5cm"> <fo:list-item-label> <fo:block font-size="12pt" font-family="sans-serif">*</fo:block> </fo:list-item-label> <fo:list-item-body> <fo:block font-size="12pt" font-family="sans-serif" font-style="italic">Remain in Light</fo:block> </fo:list-item-body> </fo:list-item> <fo:list-item space-after.optimum="17pt" start-indent="0.5cm"> <fo:list-item-label> <fo:block font-size="12pt" font-family="sans-serif">*</fo:block> </fo:list-item-label> <fo:list-item-body> <fo:block font-size="12pt" font-family="sans-serif" font-style="italic">The Joshua Tree</fo:block> </fo:list-item-body> </fo:list-item> <fo:list-item space-after.optimum="17pt" start-indent="0.5cm"> <fo:list-item-label> <fo:block font-size="12pt" font-family="sans-serif">*</fo:block> </fo:list-item-label> <fo:list-item-body> <fo:block font-size="12pt" font-family="sans-serif" font-style="italic">The Indestructible Beat of Soweto</fo:block> </fo:list-item-body> </fo:list-item> </fo:list-block> </fo:flow> </fo:page-sequence> </fo:root>
Be aware that as of this writing (May 2001), the XSL-FO specification isn't final, so there's no guarantee that these formatting objects will work correctly with future XSL-FO tools. Here's how we invoke the Apache XML Project's FOP (Formatting Objects to PDF translator) tool to create a PDF:
java org.apache.fop.apps.CommandLine test.fo test.pdf
The FOP tool creates a PDF that looks like Figure A-4.
<xsl:call-template> | Lets you invoke a particular template by name. This invocation is a convenient way to create commonly used output. For example, if you create an HTML page and all your HTML pages have the same masthead and footer, you could define templates named masthead and footer, then use <xsl:call-template> to invoke those templates as needed. |
Instruction
None.
This element can contain any number of optional <xsl:with-param> elements.
<xsl:call-template> appears inside a template.
XSLT section 6, Named Templates.
The <xsl:call-template> element gives you an excellent way to create modular stylesheets. In our case study (see Chapter 9, "Case Study: The Toot-O-Matic"), we need to generate common items at the top and bottom of every HTML page we generate. We build a navigation bar and title bar at the top of each panel in a similar way. Rather than intermingle these templates with the rest of our stylesheets, we put the templates for the common sections of the HTML pages in a separate stylesheet, then reference them when needed.
<xsl:call-template name="dw-masthead"/> <xsl:call-template name="dw-title-bar"/> <xsl:call-template name="dw-nav-bar"> <xsl:with-param name="includeMain" select="'youBetcha'"/> <xsl:with-param name="sectionNumber" select="$sectionNumber"/> <xsl:with-param name="position" select="$pos"/> <xsl:with-param name="last" select="$last"/> <xsl:with-param name="topOrBottom" select="'top'"/> <xsl:with-param name="oneOrTwo" select="'two'"/> </xsl:call-template> <!-- Processing for the main body of the page goes here --> <xsl:call-template name="dw-nav-bar"> <xsl:with-param name="includeMain" select="'youBetcha'"/> <xsl:with-param name="sectionNumber" select="$sectionNumber"/> <xsl:with-param name="position" select="$pos"/> <xsl:with-param name="last" select="$last"/> <xsl:with-param name="topOrBottom" select="'bottom'"/> <xsl:with-param name="oneOrTwo" select="'two'"/> </xsl:call-template> <xsl:call-template name="dw-footer"/>
In this code fragment, we've invoked four templates to generate the look and feel we want our HTML pages to have. If we decide to change the look and feel of our tutorials, changing those four named templates lets us change the look and feel by simply transforming the XML document again. See Section 9.5.5, "Generating the Individual Panels" in Chapter 9, "Case Study: The Toot-O-Matic" for details on how this works.
<xsl:choose> | The <xsl:choose> element is XSLT's version of the switch or case statement found in many procedural programming languages. |
Instruction
None.
None.
Contains one or more <xsl:when> elements and might contain a single <xsl:otherwise> element. Any <xsl:otherwise> elements must be the last element inside <xsl:choose>.
<xsl:choose> appears inside a template.
XSLT section 9.2, Conditional Processing with xsl:choose.
Here's an example that uses <xsl:choose> to select the background color for the rows of an HTML table. We cycle among four different values, using <xsl:choose> to determine the value of the bgcolor attribute in the generated HTML document. Here's the XML document we'll use:
<?xml version="1.0"?> <list xml:lang="en"> <title>Albums I've bought recently:</title> <listitem>The Sacred Art of Dub</listitem> <listitem>Only the Poor Man Feel It</listitem> <listitem>Excitable Boy</listitem> <listitem xml:lang="sw">Aki Special</listitem> <listitem xml:lang="en-gb">Combat Rock</listitem> <listitem xml:lang="zu">Talking Timbuktu</listitem> <listitem xml:lang="jz">The Birth of the Cool</listitem> </list>
And here's our stylesheet:
<?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="html"/> <xsl:template match="/"> <html> <head> <title> <xsl:value-of select="list/title"/> </title> </head> <body> <h1><xsl:value-of select="list/title"/></h1> <table border="1"> <xsl:for-each select="list/listitem"> <tr> <td> <xsl:attribute name="bgcolor"> <xsl:choose> <xsl:when test="position() mod 4 = 0"> <xsl:text>papayawhip</xsl:text> </xsl:when> <xsl:when test="position() mod 4 = 1"> <xsl:text>mintcream</xsl:text> </xsl:when> <xsl:when test="position() mod 4 = 2"> <xsl:text>lavender</xsl:text> </xsl:when> <xsl:otherwise> <xsl:text>whitesmoke</xsl:text> </xsl:otherwise> </xsl:choose> </xsl:attribute> <xsl:value-of select="."/> </td> </tr> </xsl:for-each> </table> </body> </html> </xsl:template> </xsl:stylesheet>
We use <xsl:choose> to determine the background color of each generated <td> element. Here's the generated HTML document, which cycles through the various background colors:
<html> <head> <META http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Albums I've bought recently:</title> </head> <body> <h1>Albums I've bought recently:</h1> <table border="1"> <tr> <td bgcolor="mintcream">The Sacred Art of Dub</td> </tr> <tr> <td bgcolor="lavender">Only the Poor Man Feel It</td> </tr> <tr> <td bgcolor="whitesmoke">Excitable Boy</td> </tr> <tr> <td bgcolor="papayawhip">Aki Special</td> </tr> <tr> <td bgcolor="mintcream">Combat Rock</td> </tr> <tr> <td bgcolor="lavender">Talking Timbuktu</td> </tr> <tr> <td bgcolor="whitesmoke">The Birth of the Cool</td> </tr> </table> </body> </html>
When rendered, our HTML document looks like Figure A-5.
<xsl:comment> | Allows you to create a comment in the output document. Comments are sometimes used to add legal notices, disclaimers, or information about when the output document was created. Another useful application of the <xsl:comment> element is the generation of CSS definitions or JavaScript code in an HTML document. |
Instruction
None.
None.
An XSLT template.
<xsl:comment> appears in a template.
XSLT section 7.4, Creating Comments.
Here's a stylesheet that generates a comment to define CSS styles in an HTML document:
<?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="html"/> <xsl:template match="/"> <html> <head> <title>XSLT and CSS Demo</title> <style> <xsl:comment> p.big {font-size: 125%; font-weight: bold} p.green {color: green; font-weight: bold} p.red {color: red; font-style: italic} </xsl:comment> </style> </head> <body> <xsl:apply-templates select="list/title"/> <xsl:apply-templates select="list/listitem"/> </body> </html> </xsl:template> <xsl:template match="title"> <p class="big"><xsl:value-of select="."/></p> </xsl:template> <xsl:template match="listitem"> <xsl:choose> <xsl:when test="position() mod 2"> <p class="green"><xsl:value-of select="."/></p> </xsl:when> <xsl:otherwise> <p class="red"><xsl:value-of select="."/></p> </xsl:otherwise> </xsl:choose> </xsl:template> </xsl:stylesheet>
This stylesheet creates three CSS styles inside an HTML comment. We'll apply the stylesheet to this document:
<?xml version="1.0"?> <list xml:lang="en"> <title>Albums I've bought recently:</title> <listitem>The Sacred Art of Dub</listitem> <listitem>Only the Poor Man Feel It</listitem> <listitem>Excitable Boy</listitem> <listitem xml:lang="sw">Aki Special</listitem> <listitem xml:lang="en-gb">Combat Rock</listitem> <listitem xml:lang="zu">Talking Timbuktu</listitem> <listitem xml:lang="jz">The Birth of the Cool</listitem> </list>
The stylesheet will apply one CSS style to the <title> element and will alternate between two CSS styles for the <listitem>s. Here's the generated HTML:
<html> <head> <META http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>XSLT and CSS Demo</title> <style> <!-- p.big {font-size: 125%; font-weight: bold} p.green {color: green; font-weight: bold} p.red {color: red; font-style: italic} --> </style> </head> <body> <p class="big">Albums I've bought recently:</p> <p class="green">The Sacred Art of Dub</p> <p class="red">Only the Poor Man Feel It</p> <p class="green">Excitable Boy</p> <p class="red">Aki Special</p> <p class="green">Combat Rock</p> <p class="red">Talking Timbuktu</p> <p class="green">The Birth of the Cool</p> </body> </html>
When rendered, the document looks like Figure A-6.
<xsl:copy> | Makes a shallow copy of an element to the result tree. This element only copies the current node and its namespace nodes. The children of the current node and any attributes it has are not copied. |
Instruction
None.
An XSLT template.
<xsl:copy> appears in a template.
XSLT section 7.5, Copying.
We'll demonstrate <xsl:copy> with an example that copies an element to the result tree. Notice that we do not specifically request that the attribute nodes of the source document be processed, so the result tree will not contain any attributes. Here is our stylesheet:
<?xml version="1.0"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:output method="xml"/> <xsl:template match="*"> <xsl:copy> <xsl:apply-templates/> </xsl:copy> </xsl:template> </xsl:stylesheet>
We'll test our stylesheet with the following XML document:
<?xml version="1.0"?> <report> <title>Miles Flown in 2001</title> <month sequence="01"> <miles-flown>12379</miles-flown> <miles-earned>35215</miles-earned> </month> <month sequence="02"> <miles-flown>32857</miles-flown> <miles-earned>92731</miles-earned> </month> <month sequence="03"> <miles-flown>19920</miles-flown> <miles-earned>76725</miles-earned> </month> <month sequence="04"> <miles-flown>18903</miles-flown> <miles-earned>31781</miles-earned> </month> </report>
Here are the results:
<?xml version="1.0" encoding="UTF-8"?> <report> <title>Miles Flown in 2001</title> <month> <miles-flown>12379</miles-flown> <miles-earned>35215</miles-earned> </month> <month> <miles-flown>32857</miles-flown> <miles-earned>92731</miles-earned> </month> <month> <miles-flown>19920</miles-flown> <miles-earned>76725</miles-earned> </month> <month> <miles-flown>18903</miles-flown> <miles-earned>31781</miles-earned> </month> </report>
The <xsl:copy> does a shallow copy, which gives you more control over the output than the <xsl:copy-of> element does. However, you must explicitly specify any child nodes or attribute nodes you would like copied to the result tree. The <xsl:apply-templates> element selects all text, element, comment, and processing-instruction children of the current element; without this element, the result tree would contain only a single, empty <report> element. Compare this approach with the example in the <xsl:copy-of> element.
<xsl:copy-of> | Copies things to the result tree. The select attribute defines the content to be copied. If the select attribute identifies a result-tree fragment, the complete fragment is copied to the result tree. If select identifies a node-set, all nodes in the node-set are copied to the result tree in document order; unlike <xsl:copy>, the node is copied in its entirety, including any namespace nodes, attribute nodes, and child nodes. If the select attribute identifies something other than a result-tree fragment or a node-set, it is converted to a string and inserted into the result tree. |
Instruction
None.
None. <xsl:copy-of> is an empty element.
<xsl:copy-of> appears inside a template.
XSLT section 11.3, Using Values of Variables and Parameters with xsl:copy-of.
We'll demonstrate <xsl:copy-of> with a simple stylesheet that copies the input document to the result tree. Here is our stylesheet:
<?xml version="1.0"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:output method="xml"/> <xsl:template match="/"> <xsl:copy-of select="."/> </xsl:template> </xsl:stylesheet>
We'll test our stylesheet with the following document:
<?xml version="1.0"?> <list> <title>A few of my favorite albums</title> <listitem>A Love Supreme</listitem> <listitem>Beat Crazy</listitem> <listitem>Here Come the Warm Jets</listitem> <listitem>Kind of Blue</listitem> <listitem>London Calling</listitem> <listitem>Remain in Light</listitem> <listitem>The Joshua Tree</listitem> <listitem>The Indestructible Beat of Soweto</listitem> </list>
When we transform the XML document, the results are strikingly similar to the input document:
<?xml version="1.0" encoding="UTF-8"?> <list> <title>A few of my favorite albums</title> <listitem>A Love Supreme</listitem> <listitem>Beat Crazy</listitem> <listitem>Here Come the Warm Jets</listitem> <listitem>Kind of Blue</listitem> <listitem>London Calling</listitem> <listitem>Remain in Light</listitem> <listitem>The Joshua Tree</listitem> <listitem>The Indestructible Beat of Soweto</listitem> </list>
The only difference between the two documents is that the stylesheet engine has added an encoding to the XML declaration. Compare this to the example in the <xsl:copy> element.
<xsl:decimal-format> | Defines a number format to be used when writing numeric values to the output document. If the <decimal-format> does not have a name, it is assumed to be the default number format used for all output. On the other hand, if a number format is named, it can be referenced from the format-number() function. |
Top-level element
None.
None. <xsl:decimal-format> is an empty element.
<xsl:decimal-format> is a top-level element and can only appear as a child of <xsl:stylesheet>.
XSLT section 12.3, Number Formatting.
Here is a stylesheet that defines two <decimal-format>s:
<?xml version="1.0" encoding="ISO-8859-1" ?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:months="Lookup table for month names"> <xsl:output method="text"/> <months:name sequence="01">January</months:name> <months:name sequence="02">February</months:name> <months:name sequence="03">March</months:name> <months:name sequence="04">April</months:name> <xsl:variable name="newline"> <xsl:text> </xsl:text> </xsl:variable> <xsl:decimal-format name="f1" decimal-separator=":" grouping-separator="/"/> <xsl:decimal-format name="f2" infinity="Really, really big" NaN="[not a number]"/> <xsl:template match="/"> <xsl:value-of select="$newline"/> <xsl:text>Tests of the <decimal-format> element:</xsl:text> <xsl:value-of select="$newline"/> <xsl:value-of select="$newline"/> <xsl:text> format-number(1528.3, '#/###:00', 'f1')=</xsl:text> <xsl:value-of select="format-number(1528.3, '#/###:00;-#/###:00', 'f1')"/> <xsl:value-of select="$newline"/> <xsl:text> format-number(1 div 0, '###,###.00', 'f2')=</xsl:text> <xsl:value-of select="format-number(1 div 0, '###,###.00', 'f2')"/> <xsl:value-of select="$newline"/> <xsl:text> format-number(blue div orange, '#.##', 'f2')=</xsl:text> <xsl:value-of select="format-number(blue div orange, '#.##', 'f2')"/> <xsl:value-of select="$newline"/> <xsl:value-of select="$newline"/> <xsl:for-each select="report/month"> <xsl:text> </xsl:text> <xsl:value-of select="document('')/*/months:name[@sequence=current()/@sequence]"/> <xsl:text> - </xsl:text> <xsl:value-of select="format-number(miles-flown, '##,###')"/> <xsl:text> miles flown, </xsl:text> <xsl:value-of select="format-number(miles-earned, '##,###')"/> <xsl:text> miles earned.</xsl:text> <xsl:value-of select="$newline"/> <xsl:text> (</xsl:text> <xsl:value-of select="format-number(miles-flown div sum(//miles-flown), '##%')"/> <xsl:text> of all miles flown, </xsl:text> <xsl:value-of select="format-number(miles-earned div sum(//miles-earned), '##%')"/> <xsl:text> of all miles earned.)</xsl:text> <xsl:value-of select="$newline"/> <xsl:value-of select="$newline"/> </xsl:for-each> <xsl:text> Total miles flown: </xsl:text> <xsl:value-of select="format-number(sum(//miles-flown), '##,###')"/> <xsl:text>, total miles earned: </xsl:text> <xsl:value-of select="format-number(sum(//miles-earned), '##,###')"/> </xsl:template> </xsl:stylesheet>
We'll use this stylesheet against the following document:
<?xml version="1.0"?> <report> <title>Miles Flown in 2001</title> <month sequence="01"> <miles-flown>12379</miles-flown> <miles-earned>35215</miles-earned> </month> <month sequence="02"> <miles-flown>32857</miles-flown> <miles-earned>92731</miles-earned> </month> <month sequence="03"> <miles-flown>19920</miles-flown> <miles-earned>76725</miles-earned> </month> <month sequence="04"> <miles-flown>18903</miles-flown> <miles-earned>31781</miles-earned> </month> </report>
When we process this document with the stylesheet, here are the results:
Tests of the <decimal-format> element: format-number(1528.3, '#/###:00', 'f1')=1/528:30 format-number(1 div 0, '###,###.00', 'f2')=Really, really big format-number(blue div orange, '#.##', 'f2')=[not a number] January - 12,379 miles flown, 35,215 miles earned. (15% of all miles flown, 15% of all miles earned.) February - 32,857 miles flown, 92,731 miles earned. (39% of all miles flown, 39% of all miles earned.) March - 19,920 miles flown, 76,725 miles earned. (24% of all miles flown, 32% of all miles earned.) April - 18,903 miles flown, 31,781 miles earned. (22% of all miles flown, 13% of all miles earned.) Total miles flown: 84,059, total miles earned: 236,452
<xsl:element> | Allows you to create an element in the output document. It works similarly to the <xsl:attribute> element. |
Instruction
An XSLT template.
<xsl:element> appears inside a template.
XSLT section 7.1.2, Creating Elements with xsl:element.
We'll use a generic stylesheet that copies the input document to the result tree, with one exception: all attributes in the original documents are converted to child elements in the result tree. The name of the new element will be the name of the format attribute, and its text will be the value of the attribute. Because we don't know the name of the attribute until we process the XML source document, we must use the <xsl:element> element to create the result tree. Here's how our stylesheet looks:
<?xml version="1.0"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:output method="xml"/> <xsl:template match="*"> <xsl:element name="{name()}"> <xsl:for-each select="@*"> <xsl:element name="{name()}"> <xsl:value-of select="."/> </xsl:element> </xsl:for-each> <xsl:apply-templates select="*|text()"/> </xsl:element> </xsl:template> </xsl:stylesheet>
This stylesheet uses the <xsl:element> element in two places: first to create a new element with the same name as the original element, and second to create a new element with the same name as each attribute. We'll apply the stylesheet to this document:
<?xml version="1.0"?> <report> <title>Miles Flown in 2001</title> <month sequence="01"> <miles-flown>12379</miles-flown> <miles-earned>35215</miles-earned> </month> <month sequence="02"> <miles-flown>32857</miles-flown> <miles-earned>92731</miles-earned> </month> <month sequence="03"> <miles-flown>19920</miles-flown> <miles-earned>76725</miles-earned> </month> <month sequence="04"> <miles-flown>18903</miles-flown> <miles-earned>31781</miles-earned> </month> </report>
Here are our results:
<?xml version="1.0" encoding="UTF-8"?> <report> <title>Miles Flown in 2001</title> <month><sequence>01</sequence> <miles-flown>12379</miles-flown> <miles-earned>35215</miles-earned> </month> <month><sequence>02</sequence> <miles-flown>32857</miles-flown> <miles-earned>92731</miles-earned> </month> <month><sequence>03</sequence> <miles-flown>19920</miles-flown> <miles-earned>76725</miles-earned> </month> <month><sequence>04</sequence> <miles-flown>18903</miles-flown> <miles-earned>31781</miles-earned> </month> </report>
The <xsl:element> element created all the elements in the output document, including the <sequence> elements that were created from the sequence attributes in the original document.
<xsl:fallback> | Defines a template that should be used when an extension element can't be found. |
Instruction
None.
None.
An XSLT template.
<xsl:fallback> appears inside a template.
XSLT section 15, Fallback.
Here is a stylesheet that uses <xsl:fallback> to terminate the transformation if an extension element can't be found:
<?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:db="xalan://DatabaseExtension" extension-element-prefixes="db"> <xsl:output method="html"/> <xsl:template match="/"> <html> <head> <title><xsl:value-of select="report/title"/></title> </head> <body> <h1><xsl:value-of select="report/title"/></h1> <xsl:for-each select="report/section"> <h2><xsl:value-of select="title"/></h2> <xsl:for-each select="dbaccess"> <db:accessDatabase> <xsl:fallback> <xsl:message terminate="yes"> Database library not available! </xsl:message> </xsl:fallback> </db:accessDatabase> </xsl:for-each> </xsl:for-each> </body> </html> </xsl:template> </xsl:stylesheet>
When we use this stylesheet to transform a document, the <xsl:fallback> element is processed if the extension element can't be found:
Database library not available! Processing terminated using xsl:message
In this case, the extension element is the Java class DatabaseExtension. If, for whatever reason, that class can't be loaded, the <xsl:fallback> element is processed. Note that the <xsl:fallback> element is processed only when the extension element can't be found; if the code that implements that extension element is found, but fails, it must be handled some other way. Also be aware that the exact format of the message and the gracefulness of stylesheet termination will vary from one XSLT processor to the next.
<xsl:for-each> | Acts as XSLT's iteration operator. This element has a select attribute that selects some nodes from the current context. |
Instruction
None.
<xsl:for-each> contains a template that is evaluated against each of the selected nodes. The <xsl:for-each> element can contain one or more <xsl:sort> elements to order the selected nodes before they are processed. All <xsl:sort> elements must appear first, before the template begins.
<xsl:for-each> appears inside a template.
XSLT section 8, Repetition.
We'll demonstrate the <xsl:for-each> element with the following stylesheet:
<?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="text"/> <xsl:variable name="newline"> <xsl:text> </xsl:text> </xsl:variable> <xsl:variable name="complicatedVariable"> <xsl:choose> <xsl:when test="count(//listitem) > 10"> <xsl:text>really long list</xsl:text> </xsl:when> <xsl:when test="count(//listitem) > 5"> <xsl:text>moderately long list</xsl:text> </xsl:when> <xsl:otherwise> <xsl:text>fairly short list</xsl:text> </xsl:otherwise> </xsl:choose> </xsl:variable> <xsl:template match="/"> <xsl:value-of select="$newline"/> <xsl:text>Here is a </xsl:text> <xsl:value-of select="$complicatedVariable"/> <xsl:text>:</xsl:text> <xsl:value-of select="$newline"/> <xsl:variable name="listitems" select="list/listitem"/> <xsl:call-template name="processListitems"> <xsl:with-param name="items" select="$listitems"/> </xsl:call-template> </xsl:template> <xsl:template name="processListitems"> <xsl:param name="items"/> <xsl:for-each select="$items"> <xsl:value-of select="position()"/> <xsl:text>. </xsl:text> <xsl:value-of select="."/> <xsl:value-of select="$newline"/> </xsl:for-each> </xsl:template> </xsl:stylesheet>
In this stylesheet, we use an <xsl:param> named items to illustrate the <xsl:for-each> element. The items parameter contains some number of <listitem> elements from the XML source document; the <xsl:for-each> element iterates through all those elements and processes each one. We'll use our stylesheet with the following XML document:
<?xml version="1.0"?> <list> <title>A few of my favorite albums</title> <listitem>A Love Supreme</listitem> <listitem>Beat Crazy</listitem> <listitem>Here Come the Warm Jets</listitem> <listitem>Kind of Blue</listitem> <listitem>London Calling</listitem> <listitem>Remain in Light</listitem> <listitem>The Joshua Tree</listitem> <listitem>The Indestructible Beat of Soweto</listitem> </list>
When we run the transformation, here are the results:
Here is a moderately long list: 1. A Love Supreme 2. Beat Crazy 3. Here Come the Warm Jets 4. Kind of Blue 5. London Calling 6. Remain in Light 7. The Joshua Tree 8. The Indestructible Beat of Soweto
The <xsl:for-each> element has iterated through all the <listitem> elements from the XML source document and has processed each one.
<xsl:if> | Implements an if statement. It contains a test attribute and an XSLT template. If the test attribute evaluates to the boolean value true, the XSLT template is processed. This element implements an if statement only; if you need an if-then-else statement, use the <xsl:choose> element with a single <xsl:when> and a single <xsl:otherwise>. |
Instruction
None.
An XSLT template.
<xsl:if> appears inside a template.
XSLT section 9.1, Conditional Processing with xsl:if.
We'll illustrate the <xsl:if> element with the following stylesheet:
<?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="text"/> <xsl:variable name="newline"> <xsl:text> </xsl:text> </xsl:variable> <xsl:template match="/"> <xsl:value-of select="$newline"/> <xsl:text>Here are the odd-numbered items from the list:</xsl:text> <xsl:value-of select="$newline"/> <xsl:for-each select="list/listitem"> <xsl:if test="(position() mod 2) = 1"> <xsl:number format="1. "/> <xsl:value-of select="."/> <xsl:value-of select="$newline"/> </xsl:if> </xsl:for-each> </xsl:template> </xsl:stylesheet>
This stylesheet uses the <xsl:if> element to see if a given <listitem>'s position is an odd number. If it is, we write it to the result tree. We'll test our stylesheet with this XML document:
<?xml version="1.0"?> <list> <title>A few of my favorite albums</title> <listitem>A Love Supreme</listitem> <listitem>Beat Crazy</listitem> <listitem>Here Come the Warm Jets</listitem> <listitem>Kind of Blue</listitem> <listitem>London Calling</listitem> <listitem>Remain in Light</listitem> <listitem>The Joshua Tree</listitem> <listitem>The Indestructible Beat of Soweto</listitem> </list>
When we run this transformation, here are the results:
Here are the odd-numbered items from the list: 1. A Love Supreme 3. Here Come the Warm Jets 5. London Calling 7. The Joshua Tree
<xsl:import> | Allows you to import the templates found in another XSLT stylesheet. Unlike <xsl:include>, all templates imported with <xsl:import> have a lower priority than those in the including stylesheet. Another difference between <xsl:include> and <xsl:import> is that <xsl:include> can appear anywhere in a stylesheet, while <xsl:import> can appear only at the beginning. |
Top-level element
None.
None. <xsl:import> is an empty element.
<xsl:import> is a top-level element and can appear only as a child of <xsl:stylesheet>.
XSLT section 2.6.2, Stylesheet Import.
Here is a simple stylesheet that we'll import:
<?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="text"/> <xsl:variable name="newline"> <xsl:text> </xsl:text> </xsl:variable> <xsl:template match="/"> <xsl:value-of select="$newline"/> <xsl:apply-templates select="list/title"/> <xsl:apply-templates select="list/listitem"/> </xsl:template> <xsl:template match="title"> <xsl:value-of select="."/> <xsl:text>: </xsl:text> <xsl:value-of select="$newline"/> <xsl:value-of select="$newline"/> </xsl:template> <xsl:template match="listitem"> <xsl:text>HERE IS LISTITEM NUMBER </xsl:text> <xsl:value-of select="position()"/> <xsl:text>: </xsl:text> <xsl:value-of select="."/> <xsl:value-of select="$newline"/> </xsl:template> </xsl:stylesheet>
We'll test both this stylesheet and the one that imports it with this XML document:
<?xml version="1.0"?> <list> <title>A few of my favorite albums</title> <listitem>A Love Supreme</listitem> <listitem>Beat Crazy</listitem> <listitem>Here Come the Warm Jets</listitem> <listitem>Kind of Blue</listitem> <listitem>London Calling</listitem> <listitem>Remain in Light</listitem> <listitem>The Joshua Tree</listitem> <listitem>The Indestructible Beat of Soweto</listitem> </list>
When we process our XML source document with this stylesheet, here are the results:
A few of my favorite albums: HERE IS LISTITEM NUMBER 1: A Love Supreme HERE IS LISTITEM NUMBER 2: Beat Crazy HERE IS LISTITEM NUMBER 3: Here Come the Warm Jets HERE IS LISTITEM NUMBER 4: Kind of Blue HERE IS LISTITEM NUMBER 5: London Calling HERE IS LISTITEM NUMBER 6: Remain in Light HERE IS LISTITEM NUMBER 7: The Joshua Tree HERE IS LISTITEM NUMBER 8: The Indestructible Beat of Soweto
Now we'll use <xsl:import> in another stylesheet:
<?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:import href="listitem.xsl"/> <xsl:output method="text"/> <xsl:variable name="newline"> <xsl:text> </xsl:text> </xsl:variable> <xsl:template match="/"> <xsl:value-of select="$newline"/> <xsl:apply-templates select="list/title"/> <xsl:apply-templates select="list/listitem"/> </xsl:template> <xsl:template match="listitem"> <xsl:value-of select="position()"/> <xsl:text>. </xsl:text> <xsl:value-of select="."/> <xsl:value-of select="$newline"/> </xsl:template> </xsl:stylesheet>
Here are the results created by our second stylesheet:
A few of my favorite albums: 1. A Love Supreme 2. Beat Crazy 3. Here Come the Warm Jets 4. Kind of Blue 5. London Calling 6. Remain in Light 7. The Joshua Tree 8. The Indestructible Beat of Soweto
Notice that both stylesheets had a template with match="listitem". The template in the imported stylesheet has a lower priority, so it is not used. Only the imported stylesheet has a template with match="title", so the imported template is used for the <title> element.
<xsl:include> | Allows you to include another XSLT stylesheet. This element allows you to put common transformations in a separate stylesheet, then include the templates from that stylesheet at any time. Unlike <xsl:import>, all templates included with <xsl:include> have the same priority as those in the including stylesheet. Another difference is that <xsl:include> can appear anywhere in a stylesheet, while <xsl:import> must appear at the beginning. |
Top-level element
None.
None. <xsl:include> is an empty element.
<xsl:include> is a top-level element and can appear only as a child of <xsl:stylesheet>.
XSLT section 2.6.1, Stylesheet Inclusion.
The <xsl:include> element is a good way to break your stylesheets into smaller pieces. (Those smaller pieces are often easier to reuse.) In our case study (see Chapter 9, "Case Study: The Toot-O-Matic"), we had a number of different stylesheets, each of which contained templates for a particular purpose. Here's how our <xsl:include> elements look:
<xsl:include href="toot-o-matic-variables.xsl"/> <xsl:include href="xslt-utilities.xsl"/> <xsl:include href="dw-style.xsl"/> <xsl:include href="build-main-index.xsl"/> <xsl:include href="build-section-indexes.xsl"/> <xsl:include href="build-individual-panels.xsl"/> <xsl:include href="build-graphics.xsl"/> <xsl:include href="build-pdf-file.xsl"/> <xsl:include href="build-zip-file.xsl"/>
Segmenting your stylesheets this way can make debugging simpler, as well. In our example here, all the rules for creating a PDF file are in the stylesheet build-pdf-file.xsl. If the PDF files are not built correctly, build-pdf-file.xsl is most likely the source of the problem. All visual elements of our generated HTML pages are created in the stylesheet dw-style.xsl. If we need to change the look of all the HTML pages, changing the templates in dw-style.xsl will do the trick.
<xsl:key> | Defines an index against the current document. The element is defined with three attributes: a name, which names this index; a match, an XPath expression that describes the nodes to be indexed; and a use attribute, an XPath expression that defines the property used to create the index. |
Top-level element
None.
None. <xsl:key> is an empty element.
<xsl:key> is a top-level element and can only appear as a child of <xsl:stylesheet>.
XSLT section 12.2, Keys.
Here is a stylesheet that defines two <xsl:key> relations against an XML document:
<?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="html" indent="yes"/> <xsl:strip-space elements="*"/> <xsl:key name="language-index" match="defn" use="@language"/> <xsl:key name="term-ids" match="term" use="@id"/> <xsl:param name="targetLanguage"/> <xsl:template match="/"> <xsl:apply-templates select="glossary"/> </xsl:template> <xsl:template match="glossary"> <html> <head> <title> <xsl:text>Glossary Listing: </xsl:text> <xsl:value-of select="key('language-index', $targetLanguage)[1]/preceding-sibling::term"/> <xsl:text> - </xsl:text> <xsl:value-of select="key('language-index', $targetLanguage)[last()]/preceding-sibling::term"/> </title> </head> <body> <h1> <xsl:text>Glossary Listing: </xsl:text> <xsl:value-of select="key('language-index', $targetLanguage)[1]/ancestor::glentry/term"/> <xsl:text> - </xsl:text> <xsl:value-of select="key('language-index', $targetLanguage)[last()]/ancestor::glentry/term"/> </h1> <xsl:for-each select="key('language-index', $targetLanguage)"> <xsl:apply-templates select="ancestor::glentry"/> </xsl:for-each> </body> </html> </xsl:template> ... </xsl:stylesheet>
For a complete discussion of this stylesheet, illustrating how the <xsl:key> relations are used, see Section 5.2.3, "Stylesheets That Use the key() Function" in Chapter 5, "Creating Links and Cross-References".
<xsl:message> | Sends a message. How the message is sent can vary from one XSLT processor to the next, but it's typically written to the standard output device. This element is useful for debugging stylesheets. |
Instruction
None.
An XSLT template.
<xsl:message> appears inside a template.
XSLT section 13, Messages.
Here's a stylesheet that uses the <xsl:message> element to trace the transformation of an XML document. We'll use our list of recently purchased albums again:
<?xml version="1.0"?> <list xml:lang="en"> <title>Albums I've bought recently:</title> <listitem>The Sacred Art of Dub</listitem> <listitem>Only the Poor Man Feel It</listitem> <listitem>Excitable Boy</listitem> <listitem xml:lang="sw">Aki Special</listitem> <listitem xml:lang="en-gb">Combat Rock</listitem> <listitem xml:lang="zu">Talking Timbuktu</listitem> <listitem xml:lang="jz">The Birth of the Cool</listitem> </list>
We'll list all of the purchased albums in an HTML table, with the background color of each row alternating through various colors. Our stylesheet uses an <xsl:choose> element inside an <xsl:attribute> element to determine the value of the bgcolor attribute. If a given <listitem> is converted to an HTML <tr> with a background color of lavender, we'll issue a celebratory message:
<?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="html"/> <xsl:template match="/"> <html> <head> <title> <xsl:value-of select="list/title"/> </title> </head> <body> <h1><xsl:value-of select="list/title"/></h1> <table border="1"> <xsl:for-each select="list/listitem"> <tr> <td> <xsl:attribute name="bgcolor"> <xsl:choose> <xsl:when test="position() mod 4 = 0"> <xsl:text>papayawhip</xsl:text> </xsl:when> <xsl:when test="position() mod 4 = 1"> <xsl:text>mintcream</xsl:text> </xsl:when> <xsl:when test="position() mod 4 = 2"> <xsl:text>lavender</xsl:text> <xsl:message terminate="no"> <xsl:text>Table row #</xsl:text> <xsl:value-of select="position()"/> <xsl:text> is lavender!</xsl:text> </xsl:message> </xsl:when> <xsl:otherwise> <xsl:text>whitesmoke</xsl:text> </xsl:otherwise> </xsl:choose> </xsl:attribute> <xsl:value-of select="."/> </td> </tr> </xsl:for-each> </table> </body> </html> </xsl:template> </xsl:stylesheet>
Note that the XSLT specification doesn't define how the message is issued. When we use this stylesheet with Xalan 2.0.1, we get these results:
file:///D:/O'Reilly/XSLT/bookSamples/AppendixA/message.xsl; Line 32; Column 51; Table row #2 is lavender! file:///D:/O'Reilly/XSLT/bookSamples/AppendixA/message.xsl; Line 32; Column 51; Table row #6 is lavender!
Xalan gives us feedback on the part of the stylesheet that generated each message. Saxon, on the other hand, keeps things short and sweet:
Table row #2 is lavender! Table row #6 is lavender!
For variety's sake, here's how XT processes the <xsl:message> element:
file:/D:/O'Reilly/XSLT/bookSamples/AppendixA/test4.xml:5: Table row #2 is lavender! file:/D:/O'Reilly/XSLT/bookSamples/AppendixA/test4.xml:9: Table row #6 is lavender!
XT gives information about the line in the XML source document that generated the message.
<xsl:namespace-alias> | Allows you to define an alias for a namespace when using the namespace directly would complicate processing. This seldom-used element is the simplest way to write a stylesheet that generates another stylesheet. |
Top-level element
None.
None. <xsl:namespace-alias> is an empty element.
<xsl:stylesheet>. <xsl:namespace-alias> is a top-level element and can appear only as a child of <xsl:stylesheet>.
XSLT section 7.1.1, Literal Result Elements.
This element is not used frequently, and the reasons for its existence are based on the somewhat obscure case of an XSLT stylesheet that needs to generate another XSLT stylesheet. Our test case here creates a stylesheet that generates the identity transform, a stylesheet that simply copies any input document to the result tree. Here's our original stylesheet that uses the namespace alias:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xslout="(the namespace URI doesn't matter here)"> <xsl:output method="xml" indent="yes"/> <xsl:namespace-alias stylesheet-prefix="xslout" result-prefix="xsl"/> <xsl:template match="/"> <xslout:stylesheet version="1.0"> <xslout:output method="xml"/> <xslout:template match="/"> <xslout:copy-of select="."/> </xslout:template> </xslout:stylesheet> </xsl:template> </xsl:stylesheet>
When we run this stylesheet with any XML document at all, we get a new stylesheet:
<?xml version="1.0" encoding="UTF-8"?> <xslout:stylesheet xmlns:xslout="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xslout:output method="xml"/> <xslout:template match="/"> <xslout:copy-of select="."/> </xslout:template> </xslout:stylesheet>
You can take this generated stylesheet and use it to copy any XML document. In our original stylesheet, we use an <xsl:namespace-alias> because we have no other way of identifying to the XSLT processor with which XSLT elements should be processed and which ones should be treated as literals passed to the output. Using the namespace alias lets us generate the XSLT elements we need in our output. Notice in the result document that the correct namespace value was declared automatically on the <xslout:stylesheet> element.
<xsl:number> | Counts something. It is most often used to number parts of a document, although it can also be used to format a numeric value. |
Instruction
None.
In all of these cases, if the from attribute is used, the only ancestors that are examined are descendants of the nearest ancestor that matches the from attribute. In other words, with from="h1", the only nodes considered for counting are those that appear under the nearest <h1> attribute.
None. <xsl:number> is an empty element.
<xsl:number> appears inside a template.
XSLT section 7.7, Numbering.
To fully illustrate how <xsl:number> works, we'll need an XML document with many things to count. Here's the document we'll use:
<?xml version="1.0"?> <book> <chapter> <title>Alfa Romeo</title> <sect1> <title>Bentley</title> </sect1> <sect1> <title>Chevrolet</title> <sect2> <title>Dodge</title> <sect3> <title>Eagle</title> </sect3> </sect2> </sect1> </chapter> <chapter> <title>Ford</title> <sect1> <title>GMC</title> <sect2> <title>Honda</title> <sect3> <title>Isuzu</title> </sect3> <sect3> <title>Javelin</title> </sect3> <sect3> <title>K-Car</title> </sect3> <sect3> <title>Lincoln</title> </sect3> </sect2> <sect2> <title>Mercedes</title> </sect2> <sect2> <title>Nash</title> <sect3> <title>Opel</title> </sect3> <sect3> <title>Pontiac</title> </sect3> </sect2> <sect2> <title>Quantum</title> <sect3> <title>Rambler</title> </sect3> <sect3> <title>Studebaker</title> </sect3> </sect2> </sect1> <sect1> <title>Toyota</title> <sect2> <title>Um, is there a car that starts with "U"?</title> </sect2> </sect1> <sect1> <title>Volkswagen</title> </sect1> </chapter> </book>
We'll use <xsl:number> in several different ways to illustrate the various options we have in numbering things. We'll look at the stylesheet and the results, then we'll discuss them. Here's the stylesheet:
<?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="text"/> <xsl:variable name="newline"> <xsl:text> </xsl:text> </xsl:variable> <xsl:template match="/"> <xsl:value-of select="$newline"/> <xsl:apply-templates select="book" mode="number-1"/> <xsl:apply-templates select="book" mode="number-2"/> <xsl:apply-templates select="book" mode="number-3"/> <xsl:apply-templates select="book" mode="number-4"/> <xsl:apply-templates select="book" mode="number-5"/> <xsl:apply-templates select="book" mode="number-6"/> <xsl:apply-templates select="book" mode="number-7"/> </xsl:template> <xsl:template match="book" mode="number-1"> <xsl:text>Test #1: level="multiple", count="chapter|sect1|sect2|sect3", format="1.1.1.1. "</xsl:text> <xsl:value-of select="$newline"/> <xsl:value-of select="$newline"/> <xsl:for-each select="chapter|.//sect1|.//sect2|.//sect3"> <xsl:number level="multiple" count="chapter|sect1|sect2|sect3" format="1.1.1.1. "/> <xsl:value-of select="title"/> <xsl:value-of select="$newline"/> </xsl:for-each> <xsl:value-of select="$newline"/> </xsl:template> <xsl:template match="book" mode="number-2"> <xsl:text>Test #2: level="any", count="chapter|sect1|sect2|sect3", format="1. "</xsl:text> <xsl:value-of select="$newline"/> <xsl:value-of select="$newline"/> <xsl:for-each select="chapter|.//sect1|.//sect2|.//sect3"> <xsl:number level="any" count="chapter|sect1|sect2|sect3" format="1. "/> <xsl:value-of select="title"/> <xsl:value-of select="$newline"/> </xsl:for-each> <xsl:value-of select="$newline"/> </xsl:template> <xsl:template match="book" mode="number-3"> <xsl:text>Test #3: level="single", count="chapter|sect1|sect2|sect3", format="1.1.1.1. "</xsl:text> <xsl:value-of select="$newline"/> <xsl:value-of select="$newline"/> <xsl:for-each select="chapter|.//sect1|.//sect2|.//sect3"> <xsl:number level="single" count="chapter|sect1|sect2|sect3" format="1.1.1.1. "/> <xsl:value-of select="title"/> <xsl:value-of select="$newline"/> </xsl:for-each> <xsl:value-of select="$newline"/> </xsl:template> <xsl:template match="book" mode="number-4"> <xsl:text>Test #4: level="multiple", select=".//sect2", count="chapter|sect1|sect2", format="I-A-i: "</xsl:text> <xsl:value-of select="$newline"/> <xsl:value-of select="$newline"/> <xsl:for-each select=".//sect2"> <xsl:number level="multiple" count="chapter|sect1|sect2" format="I-A-i: "/> <xsl:value-of select="title"/> <xsl:value-of select="$newline"/> </xsl:for-each> <xsl:value-of select="$newline"/> </xsl:template> <xsl:template match="book" mode="number-5"> <xsl:text>Test #5: level="any", count="[various elements]" from="[various elements]" format="1.1.1.1. "</xsl:text> <xsl:value-of select="$newline"/> <xsl:value-of select="$newline"/> <xsl:for-each select=".//sect3"> <xsl:number level="any" from="book" count="chapter" format="1."/> <xsl:number level="any" from="chapter" count="sect1" format="1."/> <xsl:number level="any" from="sect1" count="sect2" format="1."/> <xsl:number level="any" from="sect2" count="sect3" format="1. "/> <xsl:value-of select="title"/> <xsl:value-of select="$newline"/> </xsl:for-each> <xsl:value-of select="$newline"/> </xsl:template> <xsl:template match="book" mode="number-6"> <xsl:text>Test #6: level="any", count="chapter|sect1|sect2|sect3", grouping-separator=",", using a variable to start counting at 1000.</xsl:text> <xsl:value-of select="$newline"/> <xsl:value-of select="$newline"/> <xsl:for-each select="chapter|.//sect1|.//sect2|.//sect3"> <xsl:variable name="value1"> <xsl:number level="any" count="chapter|sect1|sect2|sect3"/> </xsl:variable> <xsl:number value="$value1 + 999" grouping-separator="." grouping-size="3"/> <xsl:text>. </xsl:text> <xsl:value-of select="title"/> <xsl:value-of select="$newline"/> </xsl:for-each> <xsl:value-of select="$newline"/> </xsl:template> <xsl:template match="book" mode="number-7"> <xsl:text>Test #7: level="multiple", count="chapter|sect1|sect2|sect3", format="1.1.1.1. ", selecting up to the first two <sect1> elements from chapter 2.</xsl:text> <xsl:value-of select="$newline"/> <xsl:value-of select="$newline"/> <xsl:for-each select="chapter[2]/sect1[position() < 3]"> <xsl:for-each select="chapter|.//sect1|.//sect2|.//sect3"> <xsl:number level="multiple" count="chapter|sect1|sect2|sect3" format="1.1.1.1. "/> <xsl:value-of select="title"/> <xsl:value-of select="$newline"/> </xsl:for-each> </xsl:for-each> </xsl:template> </xsl:stylesheet>
Here are our results:
Test #1: level="multiple", count="chapter|sect1|sect2|sect3", format="1.1.1.1. " 1. Alfa Romeo 1.1. Bentley 1.2. Chevrolet 1.2.1. Dodge 1.2.1.1. Eagle 2. Ford 2.1. GMC 2.1.1. Honda 2.1.1.1. Isuzu 2.1.1.2. Javelin 2.1.1.3. K-Car 2.1.1.4. Lincoln 2.1.2. Mercedes 2.1.3. Nash 2.1.3.1. Opel 2.1.3.2. Pontiac 2.1.4. Quantum 2.1.4.1. Rambler 2.1.4.2. Studebaker 2.2. Toyota 2.2.1. Um, is there a car that starts with "U"? 2.3. Volkswagen Test #2: level="any", count="chapter|sect1|sect2|sect3", format="1. " 1. Alfa Romeo 2. Bentley 3. Chevrolet 4. Dodge 5. Eagle 6. Ford 7. GMC 8. Honda 9. Isuzu 10. Javelin 11. K-Car 12. Lincoln 13. Mercedes 14. Nash 15. Opel 16. Pontiac 17. Quantum 18. Rambler 19. Studebaker 20. Toyota 21. Um, is there a car that starts with "U"? 22. Volkswagen Test #3: level="single", count="chapter|sect1|sect2|sect3", format="1.1.1.1. " 1. Alfa Romeo 1. Bentley 2. Chevrolet 1. Dodge 1. Eagle 2. Ford 1. GMC 1. Honda 1. Isuzu 2. Javelin 3. K-Car 4. Lincoln 2. Mercedes 3. Nash 1. Opel 2. Pontiac 4. Quantum 1. Rambler 2. Studebaker 2. Toyota 1. Um, is there a car that starts with "U"? 3. Volkswagen Test #4: level="multiple", select=".//sect2", count="chapter|sect1|sect2", format="I-A-i: " I-B-i: Dodge II-A-i: Honda II-A-ii: Mercedes II-A-iii: Nash II-A-iv: Quantum II-B-i: Um, is there a car that starts with "U"? Test #5: level="any", count="[various elements]" from="[various elements]" format="1.1.1.1. " 1.2.1.1. Eagle 2.1.1.1. Isuzu 2.1.1.2. Javelin 2.1.1.3. K-Car 2.1.1.4. Lincoln 2.1.3.1. Opel 2.1.3.2. Pontiac 2.1.4.1. Rambler 2.1.4.2. Studebaker Test #6: level="any", count="chapter|sect1|sect2|sect3", grouping-separator=",", using a variable to start counting at 1000. 1,000. Alfa Romeo 1,001. Bentley 1,002. Chevrolet 1,003. Dodge 1,004. Eagle 1,005. Ford 1,006. GMC 1,007. Honda 1,008. Isuzu 1,009. Javelin 1,010. K-Car 1,011. Lincoln 1,012. Mercedes 1,013. Nash 1,014. Opel 1,015. Pontiac 1,016. Quantum 1,017. Rambler 1,018. Studebaker 1,019. Toyota 1,020. Um, is there a car that starts with "U"? 1,021. Volkswagen Test #7: level="multiple", count="chapter|sect1|sect2|sect3", format="1.1.1.1. ", selecting up to the first two <sect1> elements from chapter 2. 2.1. GMC 2.1.1. Honda 2.1.1.1. Isuzu 2.1.1.2. Javelin 2.1.1.3. K-Car 2.1.1.4. Lincoln 2.1.2. Mercedes 2.1.3. Nash 2.1.3.1. Opel 2.1.3.2. Pontiac 2.1.4. Quantum 2.1.4.1. Rambler 2.1.4.2. Studebaker 2.2. Toyota 2.2.1. Um, is there a car that starts with "U"?
In Test 1, we used level="multiple" to count the <chapter>, <sect1>, <sect2>, and <sect3> elements. Numbering these at multiple levels gives us a dotted-decimal number for each element. We can look at the number next to Studebaker and know that it is the second <sect3> element inside the fourth <sect2> element inside the first <sect1> element inside the second <chapter> element.
Test 2 uses level="any" to count all of the <chapter>, <sect1>, <sect2>, and <sect3> elements in order.
Test 3 uses level="single" to count the elements at each level. This means that the fourth <sect3> element inside a given <sect2> element will be numbered with a 4 (or iv or D or whatever the appropriate value would be). Notice that the number used for each element is the same as the last number beside each element in Test 1.
Test 4 does a couple of things differently: first, it uses the uppercase-alpha and lowercase-roman numbering styles. Second, it counts elements at multiple levels (for the <chapter>, <sect1>, and <sect2> elements), but we only process the <sect2> elements. Even though we only output the title text for the <sect2> elements, we can still generate the appropriate multilevel numbers.
Test 5 generates numbers similarly to Test 4, except that it uses the from attribute. We generate numbers for <sect3> elements in four stages; first, we count the <chapter> ancestors, starting at the first <book> ancestor; then we count the <sect1> ancestors, starting at the first <chapter> ancestor, etc.
Test 6 starts counting at 1000 instead of 1. To do this, we have to store the value generated by <xsl:number> in a variable, then output the value of the variable plus 1000. Notice that we can use an expression in the value attribute of the <xsl:number> element. We also used the grouping-separator attribute to use a comma to separate groups of three digits.
Last but not least, Test 7 only numbers items from the first and second <sect1> elements (<sect1> elements whose position() is less than 3) in the second <chapter> element. Even though we're only processing these sections, we can still use <xsl:number> to generate the correct numbers for the elements.
<xsl:otherwise> | Defines the else or default case in an <xsl:choose> element. This element always appears inside an <xsl:choose> element, and it must always appear last. |
Subinstruction (<xsl:otherwise> always appears as part of an <xsl:choose> element).
None.
None.
A template.
The <xsl:choose> element.
XSLT section 9.2, Conditional Processing with xsl:choose.
As an example, we'll use an <xsl:choose> element that cycles through a set of values for the background color of a cell in an HTML table. We'll use this XML document as our input:
<?xml version="1.0"?> <list xml:lang="en"> <title>Albums I've bought recently:</title> <listitem>The Sacred Art of Dub</listitem> <listitem>Only the Poor Man Feel It</listitem> <listitem>Excitable Boy</listitem> <listitem xml:lang="sw">Aki Special</listitem> <listitem xml:lang="en-gb">Combat Rock</listitem> <listitem xml:lang="zu">Talking Timbuktu</listitem> <listitem xml:lang="jz">The Birth of the Cool</listitem> </list>
Here is our stylesheet, which uses <xsl:choose> inside an <xsl:attribute> element to determine the correct value for the bgcolor attribute. We have an <xsl:otherwise> element that generates the value whitesmoke for every fourth <listitem> in our source document:
<?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="html"/> <xsl:template match="/"> <html> <head> <title> <xsl:value-of select="list/title"/> </title> </head> <body> <h1><xsl:value-of select="list/title"/></h1> <table border="1"> <xsl:for-each select="list/listitem"> <tr> <td> <xsl:attribute name="bgcolor"> <xsl:choose> <xsl:when test="@bgcolor"> <xsl:value-of select="@bgcolor"/> </xsl:when> <xsl:when test="position() mod 4 = 0"> <xsl:text>papayawhip</xsl:text> </xsl:when> <xsl:when test="position() mod 4 = 1"> <xsl:text>mintcream</xsl:text> </xsl:when> <xsl:when test="position() mod 4 = 2"> <xsl:text>lavender</xsl:text> </xsl:when> <xsl:otherwise> <xsl:text>whitesmoke</xsl:text> </xsl:otherwise> </xsl:choose> </xsl:attribute> <xsl:value-of select="."/> </td> </tr> </xsl:for-each> </table> </body> </html> </xsl:template> </xsl:stylesheet>
Here is our generated HTML document. Notice that every fourth row has a background color of whitesmoke; that value was generated by the <xsl:otherwise> element:
<html> <head> <META http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Albums I've bought recently:</title> </head> <body> <h1>Albums I've bought recently:</h1> <table border="1"> <tr> <td bgcolor="mintcream">The Sacred Art of Dub</td> </tr> <tr> <td bgcolor="lavender">Only the Poor Man Feel It</td> </tr> <tr> <td bgcolor="whitesmoke">Excitable Boy</td> </tr> <tr> <td bgcolor="papayawhip">Aki Special</td> </tr> <tr> <td bgcolor="mintcream">Combat Rock</td> </tr> <tr> <td bgcolor="lavender">Talking Timbuktu</td> </tr> <tr> <td bgcolor="whitesmoke">The Birth of the Cool</td> </tr> </table> </body> </html>
When rendered, our HTML document looks like Figure A-7.
<xsl:output> | Defines the characteristics of the output document. |
Top-level element
None.
None. <xsl:output> is an empty element.
<xsl:output> is a top-level element and can only appear as a child of <xsl:stylesheet>.
XSLT section 16, Output.
To illustrate the three output methods defined in the XSLT specification, we'll create three stylesheets, each of which uses one of the three methods. We'll use the following XML document in all three examples:
<?xml version="1.0"?> <list> <title>A few of my favorite albums</title> <listitem>A Love Supreme</listitem> <listitem>Beat Crazy</listitem> <listitem>Here Come the Warm Jets</listitem> <listitem>Kind of Blue</listitem> <listitem>London Calling</listitem> <listitem>Remain in Light</listitem> <listitem>The Joshua Tree</listitem> <listitem>The Indestructible Beat of Soweto</listitem> </list>
We'll now look at our three stylesheets and the results produced by each. First, let's look at the method="xml" stylesheet:
<?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml" doctype-public="-//W3C/DTD XHTML 1.0//EN" doctype-system="file:///d:/xhtml.dtd" encoding="ISO-8859-1" indent="no"/> <xsl:template match="/"> <html> <head> <title><xsl:value-of select="/list/title"/></title> </head> <body> <h1><xsl:value-of select="/list/title"/></h1> <p> <xsl:for-each select="/list/listitem"> <xsl:number format="1. "/> <xsl:value-of select="."/> <br/> </xsl:for-each> </p> </body> </html> </xsl:template> </xsl:stylesheet>
This stylesheet generates the following results:
<?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE html PUBLIC "-//W3C/DTD XHTML 1.0//EN" "file:///d:/xhtml.dtd"> <html><head><title>A few of my favorite albums</title> </head><body><h1>A few of my favorite albums</h1> <p>1. A Love Supreme<br/>2. Beat Crazy<br/>3. Here Come the Warm Jets<br/>4. Kind of Blue<br/>5. London Calling<br/>6. Remain in Light<br/>7. The Joshua Tree<br/>8. The Indestructible Beat of Soweto<br/></p></body></html>
(We actually added line breaks to this listing; the original output put everything from <html> through </html> on a single line.)
The output document has the encoding we specified in our stylesheet, and the DOCTYPE declaration includes the PUBLIC and SYSTEM identifiers we requested as well. Even with the line breaks we added, it's still obvious that this document has not been formatted with any extra whitespace whatsoever. We also have empty <br/> elements in our output document; those elements will be handled differently when we specify method="html". Speaking of which, here is our method="html" stylesheet:
<?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="html" encoding="ISO-8859-1" doctype-public="-//W3C/DTD HTML 1.0 Transitional//EN"/> <xsl:template match="/"> <html> <head> <title><xsl:value-of select="/list/title"/></title> </head> <body> <h1><xsl:value-of select="/list/title"/></h1> <p> <xsl:for-each select="/list/listitem"> <xsl:number format="1. "/> <xsl:value-of select="."/> <br/> </xsl:for-each> </p> </body> </html> </xsl:template> </xsl:stylesheet>
Here is the HTML document generated by this stylesheet:
<!DOCTYPE HTML PUBLIC "-//W3C/DTD HTML 1.0 Transitional//EN"> <html> <head> <META http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>A few of my favorite albums</title> </head> <body> <h1>A few of my favorite albums</h1> <p>1. A Love Supreme<br>2. Beat Crazy<br>3. Here Come the Warm Jets<br>4. Kind of Blue<br>5. London Calling<br>6. Remain in Light<br>7. The Joshua Tree<br>8. The Indestructible Beat of Soweto<br> </p> </body> </html>
(As before, we added line breaks to make the listing legible.) Notice that the XSLT processor has automatically inserted a <META> element in the <head> of our HTML document. The <br> elements that were empty in our previous stylesheet are now old-fashioned <br> tags. Even though this style of XSLT output results in a document that is not valid XML (or XHTML), the document will work with existing HTML browsers.
Our final stylesheet will use method="text":
<?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="text"/> <xsl:template match="/"> <html> <head> <title><xsl:value-of select="/list/title"/></title> </head> <body> <h1><xsl:value-of select="/list/title"/></h1> <p> <xsl:for-each select="/list/listitem"> <xsl:number format="1. "/> <xsl:value-of select="."/> <br/> </xsl:for-each> </p> </body> </html> </xsl:template> </xsl:stylesheet>
Here are the results, such as they are, from this stylesheet:
A few of my favorite albumsA few of my favorite albums1. A Love Supreme2. Beat Crazy3. Here Come the Warm Jets4. Kind of Blue5. London Calling6. Remain in Light7. The Joshua Tree8. The Indestructible Beat of Soweto
(As before, we inserted line breaks so the document would fit on the page.) These results are basically worthless. Why weren't our carefully coded HTML elements output to the text document? The reason is that the text output method only outputs text nodes to the result tree. Even though we requested that various HTML elements be generated along the way, they're ignored because we specified method="text".
<xsl:param> | Defines the name and value of a parameter to be used by a template. This element can appear as a top-level element or inside the <xsl:template> element. If the <xsl:param> appears as a top-level element, it is a global parameter, visible to all areas of the stylesheet. The value of the parameter can be defined in one of two ways: specified in the select attribute, or defined in an XSLT template inside the <xsl:param> element itself. |
Instruction
If the select attribute is used, <xsl:param> should be empty. Otherwise, it contains an XSLT template.
<xsl:stylesheet> and <xsl:template>. If an <xsl:param> appears as a child of <xsl:stylesheet>, then it is a global parameter visible throughout the stylesheet. XSLT doesn't define the way global parameters are passed to the XSLT processor, so check the documentation for your processor to see how this is done. (See Section 4.4.3, "Global Parameters" in Chapter 4, "Branching and Control Elements" for an overview of how to pass parameters to the most popular XSLT processors.)
XSLT section 11, Variables and Parameters.
Here is a stylesheet that defines several <xsl:param> elements, both global and local. Notice that one of the parameters is a node-set; parameters can be of any XPath or XSLT datatype:
<?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="text"/> <xsl:variable name="newline"> <xsl:text> </xsl:text> </xsl:variable> <xsl:param name="favoriteNumber" select="23"/> <xsl:param name="favoriteColor"/> <xsl:template match="/"> <xsl:value-of select="$newline"/> <xsl:value-of select="list/title"/> <xsl:value-of select="$newline"/> <xsl:variable name="listitems" select="list/listitem"/> <xsl:call-template name="processListitems"> <xsl:with-param name="items" select="$listitems"/> <xsl:with-param name="color" select="'yellow'"/> <xsl:with-param name="number" select="$favoriteNumber"/> </xsl:call-template> </xsl:template> <xsl:template name="processListitems"> <xsl:param name="items"/> <xsl:param name="color" select="'blue'"/> <xsl:for-each select="$items"> <xsl:value-of select="position()"/> <xsl:text>. </xsl:text> <xsl:value-of select="."/> <xsl:value-of select="$newline"/> </xsl:for-each> <xsl:value-of select="$newline"/> <xsl:text>Your favorite color is </xsl:text> <xsl:value-of select="$favoriteColor"/> <xsl:text>.</xsl:text> <xsl:value-of select="$newline"/> <xsl:text>The color passed to this template is </xsl:text> <xsl:value-of select="$color"/> <xsl:text>.</xsl:text> <xsl:value-of select="$newline"/> </xsl:template> </xsl:stylesheet>
We'll use this stylesheet to transform the following document:
<?xml version="1.0"?> <list> <title>A few of my favorite albums</title> <listitem>A Love Supreme</listitem> <listitem>Beat Crazy</listitem> <listitem>Here Come the Warm Jets</listitem> <listitem>Kind of Blue</listitem> <listitem>London Calling</listitem> <listitem>Remain in Light</listitem> <listitem>The Joshua Tree</listitem> <listitem>The Indestructible Beat of Soweto</listitem> </list>
Here are the results:
A few of my favorite albums 1. A Love Supreme 2. Beat Crazy 3. Here Come the Warm Jets 4. Kind of Blue 5. London Calling 6. Remain in Light 7. The Joshua Tree 8. The Indestructible Beat of Soweto Your favorite color is purple. The color passed to this template is yellow.
To generate these results, we passed the value purple to the XSLT processor. With Xalan, the value is passed like this:
java org.apache.xalan.xslt.Process -in test4.xml -xsl param.xsl -param favoriteColor purple
(The command should be entered on a single line.) See Section 4.4.3, "Global Parameters" in Chapter 4, "Branching and Control Elements" for a more complete discussion of global parameters and how they can be set for various XSLT processors.
<xsl:preserve-space> | Defines the source document elements for which whitespace should be preserved. |
Top-level element
None.
None. <xsl:preserve-space> is an empty element.
<preserve-space> is a top-level element and can only appear as a child of <xsl:stylesheet> .
XSLT section 3.4, Whitespace Stripping.
We'll illustrate how <preserve-space> works with the following stylesheet:
<?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="text"/> <xsl:preserve-space elements="listing"/> <xsl:variable name="newline"> <xsl:text> </xsl:text> </xsl:variable> <xsl:template match="/"> <xsl:value-of select="$newline"/> <xsl:value-of select="/code-sample/title"/> <xsl:value-of select="$newline"/> <xsl:for-each select="/code-sample/listing"> <xsl:value-of select="."/> </xsl:for-each> </xsl:template> </xsl:stylesheet>
We'll use this stylesheet to process the following document:
<?xml version="1.0"?> <code-sample> <title>Conditional variable initialization</title> <listing> <type>int</type> <variable>y</variable> = <constant>23</constant>; <type>int</type> <variable>x</variable>; <keyword>if</keyword> (<variable>y</variable> > <constant>10</constant>) <variable>x</variable> = <constant>5</constant>; <keyword>else</keyword> <keyword>if</keyword> (<variable>y</variable> > <constant>5</constant>) <variable>x</variable> = <constant>3</constant>; <keyword>else</keyword> <variable>x</variable> = <constant>1</constant>; </listing> </code-sample>
When we process this document with our stylesheet, we get these results:
Conditional variable initialization int y = 23; int x; if (y > 10) x = 5; else if (y > 5) x = 3; else x = 1;
Compare this example to the one for the <strip-space> element.
<xsl:processing-instruction> | Creates a processing instruction in the output document. |
Instruction
None.
An XSLT template. The contents of the template become the data of the processing instruction.
<xsl:processing-instruction> appears inside a template.
XSLT section 7.3, Creating Processing Instructions.
We'll demonstrate a stylesheet that adds a processing instruction to an XML document. The processing instruction will associate the stylesheet template.xsl with this XML document. Here is our stylesheet:
<?xml version="1.0"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:output method="xml"/> <xsl:template match="/"> <xsl:processing-instruction name="xml-stylesheet">href="docbook/html/docbook.xsl" type="text/xsl"</xsl:processing-instruction> <xsl:copy-of select="."/> </xsl:template> </xsl:stylesheet>
This stylesheet simply uses the <xsl:copy-of> element to copy the input document to the result tree, adding a processing instruction along the way. We'll use our stylesheet with this XML document:
<?xml version="1.0"?> <list> <title>A few of my favorite albums</title> <listitem>A Love Supreme</listitem> <listitem>Beat Crazy</listitem> <listitem>Here Come the Warm Jets</listitem> <listitem>Kind of Blue</listitem> <listitem>London Calling</listitem> <listitem>Remain in Light</listitem> <listitem>The Joshua Tree</listitem> <listitem>The Indestructible Beat of Soweto</listitem> </list>
When we run this transformation, here are the results:
<?xml version="1.0" encoding="UTF-8"?> <?xml-stylesheet href="docbook/html/docbook.xsl" type="text/xsl"?> <list> <title>A few of my favorite albums</title> <listitem>A Love Supreme</listitem> <listitem>Beat Crazy</listitem> <listitem>Here Come the Warm Jets</listitem> <listitem>Kind of Blue</listitem> <listitem>London Calling</listitem> <listitem>Remain in Light</listitem> <listitem>The Joshua Tree</listitem> <listitem>The Indestructible Beat of Soweto</listitem> </list>
Note that the contents of a processing instruction are text. Even though the processing instruction we just generated looks like it contains two attributes, you can't create the processing instruction like this:
<?xml version="1.0"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:output method="xml"/> <xsl:template match="/"> <xsl:processing-instruction name="xml-stylesheet"> <!-- This doesn't work! You can't put <xsl:attribute> elements inside a <xsl:processing-instruction> element. --> <xsl:attribute name="href"> <xsl:text>docbook/html/docbook.xsl</xsl:text> </xsl:attribute> <xsl:attribute name="type"> <xsl:text>text/xsl</xsl:text> </xsl:attribute> </xsl:processing-instruction> <xsl:copy-of select="."/> </xsl:template> </xsl:stylesheet>
If you try this, you'll get an exception from the XSLT processor.
<xsl:sort> | Defines a sort key for the current context. This element appears as a child of the <xsl:apply-templates> or <xsl:for-each> elements. Within those elements, the first <xsl:sort> defines the primary sort key, the second <xsl:sort> defines the secondary sort key, etc. |
Subinstruction (<xsl:sort> always appears as a child of the <xsl:apply-templates> or <xsl:for-each> elements)
None.
None.
<xsl:apply-templates> and <xsl:for-each>.
XSLT section 10, Sorting.
We'll illustrate <xsl:sort> with this stylesheet:
<?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="text"/> <xsl:variable name="newline"> <xsl:text> </xsl:text> </xsl:variable> <xsl:template match="/"> <xsl:value-of select="$newline"/> <xsl:call-template name="ascending-alpha-sort"> <xsl:with-param name="items" select="/sample/textlist/listitem"/> </xsl:call-template> <xsl:call-template name="ascending-alpha-sort"> <xsl:with-param name="items" select="/sample/numericlist/listitem"/> </xsl:call-template> <xsl:call-template name="ascending-numeric-sort"> <xsl:with-param name="items" select="/sample/numericlist/listitem"/> </xsl:call-template> <xsl:call-template name="descending-alpha-sort"> <xsl:with-param name="items" select="/sample/textlist/listitem"/> </xsl:call-template> </xsl:template> <xsl:template name="ascending-alpha-sort"> <xsl:param name="items"/> <xsl:text>Ascending text sort:</xsl:text> <xsl:value-of select="$newline"/> <xsl:for-each select="$items"> <xsl:sort select="."/> <xsl:value-of select="."/> <xsl:value-of select="$newline"/> </xsl:for-each> <xsl:value-of select="$newline"/> </xsl:template> <xsl:template name="descending-alpha-sort"> <xsl:param name="items"/> <xsl:text>Descending text sort:</xsl:text> <xsl:value-of select="$newline"/> <xsl:for-each select="$items"> <xsl:sort select="." order="descending"/> <xsl:value-of select="."/> <xsl:value-of select="$newline"/> </xsl:for-each> <xsl:value-of select="$newline"/> </xsl:template> <xsl:template name="ascending-numeric-sort"> <xsl:param name="items"/> <xsl:text>Ascending numeric sort:</xsl:text> <xsl:value-of select="$newline"/> <xsl:for-each select="$items"> <xsl:sort select="." data-type="number"/> <xsl:value-of select="."/> <xsl:value-of select="$newline"/> </xsl:for-each> <xsl:value-of select="$newline"/> </xsl:template> </xsl:stylesheet>
Our stylesheet defines three named templates, each of which sorts <listitem>s in a different order or with a different data-type. We'll use this stylesheet against this document:
<?xml version="1.0"?> <sample> <numericlist> <listitem>1</listitem> <listitem>3</listitem> <listitem>23</listitem> <listitem>120</listitem> <listitem>2</listitem> </numericlist> <textlist> <listitem>3</listitem> <listitem>apple</listitem> <listitem>orange</listitem> <listitem>dragonfruit</listitem> <listitem>carambola</listitem> </textlist> </sample>
Here are the results:
Ascending text sort: 3 apple carambola dragonfruit orange Ascending text sort: 1 120 2 23 3 Ascending numeric sort: 1 2 3 23 120 Descending text sort: orange dragonfruit carambola apple 3
Notice that the data-type="numeric" attribute causes data to be sorted in numeric order.
<xsl:strip-space> | Defines the source-document elements for which whitespace should be removed. |
Top-level element
None.
None. <xsl:strip-space> is an empty element.
<xsl:strip-space> is a top-level element, and can only appear as a child of <xsl:stylesheet> .
XSLT section 3.4, Whitespace Stripping.
We'll illustrate the <xsl:strip-space> element with the following stylesheet:
<?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="text"/> <xsl:strip-space elements="listing"/> <xsl:variable name="newline"> <xsl:text> </xsl:text> </xsl:variable> <xsl:template match="/"> <xsl:value-of select="$newline"/> <xsl:value-of select="/code-sample/title"/> <xsl:value-of select="$newline"/> <xsl:for-each select="/code-sample/listing"> <xsl:value-of select="."/> </xsl:for-each> </xsl:template> </xsl:stylesheet>
We'll use this stylesheet to process the following document:
<?xml version="1.0"?> <code-sample> <title>Conditional variable initialization</title> <listing> <type>int</type> <variable>y</variable> = <constant>23</constant>; <type>int</type> <variable>x</variable>; <keyword>if</keyword> (<variable>y</variable> > <constant>10</constant>) <variable>x</variable> = <constant>5</constant>; <keyword>else</keyword> <keyword>if</keyword> (<variable>y</variable> > <constant>5</constant>) <variable>x</variable> = <constant>3</constant>; <keyword>else</keyword> <variable>x</variable> = <constant>1</constant>; </listing> </code-sample>
Here are the results:
Conditional variable initialization inty = 23; intx; if (y > 10) x = 5; elseif (y > 5) x = 3; elsex = 1;
Notice that all the extra whitespace from the <listing> element has been removed. This includes the space between the various elements contained inside <listing>, such as <keyword>, <constant>, and <variable>. Compare this example to the one for the <preserve-space> element.
<xsl:stylesheet> | The root element of an XSLT stylesheet. It is identical to the <xsl:transform> element, which was included in the XSLT specification for historical purposes. |
Contains the entire stylesheet
Defines any namespace prefixes used to invoke extension elements. Multiple namespace prefixes are separated by whitespace.
This element contains the entire stylesheet. The following items can be children of <xsl:stylesheet>:
<xsl:import>
<xsl:include>
<xsl:strip-space>
<xsl:preserve-space>
<xsl:output>
<xsl:key>
<xsl:decimal-format>
<xsl:namespace-alias>
<xsl:attribute-set>
<xsl:variable>
<xsl:param>
<xsl:template>
None. <xsl:stylesheet> is the root element of the stylesheet.
XSLT section 2.2, Stylesheet Element.
For the sake of completeness, we'll include an example here. We'll use the Hello World document from the XML 1.0 specification for our example:
<?xml version="1.0"?> <greeting> Hello, World! </greeting>
We'll transform our document with this stylesheet:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:output method="html"/> <xsl:template match="/"> <xsl:apply-templates select="greeting"/> </xsl:template> <xsl:template match="greeting"> <html> <body> <h1> <xsl:value-of select="."/> </h1> </body> </html> </xsl:template> </xsl:stylesheet>
When we transform our document with this stylesheet, here are the results:
<html> <body> <h1> Hello, World! </h1> </body> </html>
<xsl:template> | Defines an output template. For templates that begin <xsl:template match="x", the template defines a transformation for a given element. Templates that begin <xsl:template name="x" define a set of output elements that are processed whenever the template is invoked. All <xsl:template> elements must have either the match or the name attribute defined. Although not common, it is also possible to create <xsl:template> elements that have both a match and a name. |
Top-level element
None.
An XSLT template.
<xsl:stylesheet>. <xsl:template> is a top-level element and can only appear as a child of <xsl:stylesheet>.
XSLT section 5.3, Defining Template Rules.
We'll use a template that copies all nodes from the input document to the output document, with one important difference: all attributes in the original document are converted to elements in the output document. The name of each generated element is the name of the original attribute, and the text of each element is the attribute's value. Here's our stylesheet:
<?xml version="1.0"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:output method="xml"/> <xsl:template match="*"> <xsl:element name="{name()}"> <xsl:for-each select="@*"> <xsl:element name="{name()}"> <xsl:value-of select="."/> </xsl:element> </xsl:for-each> <xsl:apply-templates select="*|text()"/> </xsl:element> </xsl:template> </xsl:stylesheet>
Our stylesheet contains a single <xsl:template> that transforms every node in the original document. We'll use our stylesheet to transform the following XML document:
<?xml version="1.0"?> <report> <title>Miles Flown in 2001</title> <month sequence="01"> <miles-flown>12379</miles-flown> <miles-earned>35215</miles-earned> </month> <month sequence="02"> <miles-flown>32857</miles-flown> <miles-earned>92731</miles-earned> </month> <month sequence="03"> <miles-flown>19920</miles-flown> <miles-earned>76725</miles-earned> </month> <month sequence="04"> <miles-flown>18903</miles-flown> <miles-earned>31781</miles-earned> </month> </report>
Here are the results of our transformation:
<?xml version="1.0" encoding="UTF-8"?> <report> <title>Miles Flown in 2001</title> <month><sequence>01</sequence> <miles-flown>12379</miles-flown> <miles-earned>35215</miles-earned> </month> <month><sequence>02</sequence> <miles-flown>32857</miles-flown> <miles-earned>92731</miles-earned> </month> <month><sequence>03</sequence> <miles-flown>19920</miles-flown> <miles-earned>76725</miles-earned> </month> <month><sequence>04</sequence> <miles-flown>18903</miles-flown> <miles-earned>31781</miles-earned> </month> </report>
<xsl:text> | Allows you to write literal text to the output document. |
Instruction
None.
#PCDATA, literal text, and entity references.
<xsl:text> appears inside a template.
XSLT section 7.2, Creating Text.
This sample stylesheet generates text with <xsl:text>. We intermingle <xsl:text> elements and <xsl:value-of> elements to create a coherent sentence. In this case, we simply generate a text document, but this technique works equally well to create the text of an HTML or XML element. Here is the stylesheet:
<?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="text"/> <xsl:variable name="newline"> <xsl:text> </xsl:text> </xsl:variable> <xsl:template match="/"> <xsl:text>Your document contains </xsl:text> <xsl:value-of select="count(//*)"/> <xsl:text> elements and </xsl:text> <xsl:value-of select="count(//@*)"/> <xsl:text> attributes. </xsl:text> <xsl:value-of select="$newline"/> <xsl:text disable-output-escaping="yes"><Have a great day!></xsl:text> </xsl:template> </xsl:stylesheet>
Also notice our use of <xsl:variable> to generate line breaks. The <xsl:text> element inside the <xsl:variable> element contains a line break, so writing the value of that variable to the result tree gives us the line break we want. Given this XML document:
<?xml version="1.0"?> <list xml:lang="en"> <title>Albums I've bought recently:</title> <listitem>The Sacred Art of Dub</listitem> <listitem>Only the Poor Man Feel It</listitem> <listitem>Excitable Boy</listitem> <listitem xml:lang="sw">Aki Special</listitem> <listitem xml:lang="en-gb">Combat Rock</listitem> <listitem xml:lang="zu">Talking Timbuktu</listitem> <listitem xml:lang="jz">The Birth of the Cool</listitem> </list>
Our stylesheet produces these results:
Your document contains 9 elements and 5 attributes. <Have a great day!>
Since we use the text output method, the disable-output-escaping attribute has no effect. If you change the stylesheet to use <xsl:output method="html"/> or <xsl:output method="xml"/>, then disable-output-escaping is used. Here are the results for disable-output-escaping="yes":
Your document contains 10 elements and 2 attributes. <Have a great day!>
And here are the results for disable-output-escaping="no", the default:
Your document contains 10 elements and 2 attributes. <Have a great day!>
<xsl:transform> | This is a synonym for <xsl:stylesheet>. It was included in the XSLT 1.0 spec for historical purposes. Its attributes, content, and all other properties are the same as those for <xsl:stylesheet>. See <xsl:stylesheet> for more information. |
<xsl:value-of> | Calculates the value of an XPath expression, converts that value to a string, and then writes the value to the result tree. |
Instruction
None. <xsl:value-of> is an empty element.
<xsl:value-of> appears inside a template.
XSLT section 7.6.1, Generating Text with xsl:value-of.
We'll use the <xsl:value-of> element to generate some text. Here is our stylesheet:
<?xsl version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="text"/> <xsl:variable name="newline"> <xsl:text> </xsl:text> </xsl:variable> <xsl:template match="/"> <xsl:text>Your document contains</xsl:text> <xsl:value-of select="count(//*)"/> <xsl:text> elements and </xsl:text> <xsl:value-of select="count(//@*)"/> <xsl:text> attributes. </xsl:text> <xsl:value-of select="$newline"/> <xsl:text>Have a great day!</xsl:text> </xsl:template> </xsl:stylesheet>
We'll use this XML document as input:
<?xml version="1.0"?> <report> <title>Miles Flown in 2001</title> <month sequence="01"> <miles-flown>12379</miles-flown> <miles-earned>35215</miles-earned> </month> <month sequence="02"> <miles-flown>32857</miles-flown> <miles-earned>92731</miles-earned> </month> <month sequence="03"> <miles-flown>19920</miles-flown> <miles-earned>76725</miles-earned> </month> <month sequence="04"> <miles-flown>18903</miles-flown> <miles-earned>31781</miles-earned> </month> </report>
Here are the results:
Your document contains 14 elements and 4 attributes. Have a great day!
<xsl:variable> | Defines a variable. If <xsl:variable> occurs as a top-level element, it is a global variable that is accessible throughout the stylesheet. Otherwise, the variable is local and exists only in the element that contains the <xsl:variable>. The value of the variable can be defined in one of two ways: specified in the select attribute or defined in an XSLT template inside the <xsl:variable> element itself. If neither method is used, the value of the variable is an empty string. |
Either a top-level element or an instruction
The <xsl:variable> element can be empty, or it can contain an XSLT template. If it contains an XSLT template, the value of the select attribute (if any exists) is ignored.
<xsl:stylesheet> as a top-level element or in a template.
XSLT section 11, Variables and Parameters.
Here is a stylesheet that defines a number of variables:
<?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="text"/> <xsl:variable name="newline"> <xsl:text> </xsl:text> </xsl:variable> <xsl:variable name="favoriteNumber" select="23"/> <xsl:variable name="favoriteColor" select="'blue'"/> <xsl:variable name="complicatedVariable"> <xsl:choose> <xsl:when test="count(//listitem) > 10"> <xsl:text>really long list</xsl:text> </xsl:when> <xsl:when test="count(//listitem) > 5"> <xsl:text>moderately long list</xsl:text> </xsl:when> <xsl:otherwise> <xsl:text>fairly short list</xsl:text> </xsl:otherwise> </xsl:choose> </xsl:variable> <xsl:template match="/"> <xsl:text>Hello! Your favorite number is </xsl:text> <xsl:value-of select="$favoriteNumber"/> <xsl:text>.</xsl:text> <xsl:value-of select="$newline"/> <xsl:text>Your favorite color is </xsl:text> <xsl:value-of select="$favoriteColor"/> <xsl:text>.</xsl:text> <xsl:value-of select="$newline"/> <xsl:value-of select="$newline"/> <xsl:text>Here is a </xsl:text> <xsl:value-of select="$complicatedVariable"/> <xsl:text>:</xsl:text> <xsl:value-of select="$newline"/> <xsl:variable name="listitems" select="list/listitem"/> <xsl:call-template name="processListitems"> <xsl:with-param name="items" select="$listitems"/> </xsl:call-template> </xsl:template> <xsl:template name="processListitems"> <xsl:param name="items"/> <xsl:variable name="favoriteColor"> <xsl:text>chartreuse</xsl:text> </xsl:variable> <xsl:text> (Your favorite color is now </xsl:text> <xsl:value-of select="$favoriteColor"/> <xsl:text>.)</xsl:text> <xsl:value-of select="$newline"/> <xsl:for-each select="$items"> <xsl:value-of select="position()"/> <xsl:text>. </xsl:text> <xsl:value-of select="."/> <xsl:value-of select="$newline"/> </xsl:for-each> </xsl:template> </xsl:stylesheet>
We'll use our stylesheet to transform the following document:
<?xml version="1.0"?> <list xml:lang="en"> <title>Albums I've bought recently:</title> <listitem>The Sacred Art of Dub</listitem> <listitem>Only the Poor Man Feel It</listitem> <listitem>Excitable Boy</listitem> <listitem xml:lang="sw">Aki Special</listitem> <listitem xml:lang="en-gb">Combat Rock</listitem> <listitem xml:lang="zu">Talking Timbuktu</listitem> <listitem xml:lang="jz">The Birth of the Cool</listitem> </list>
Here are the results of our transformation:
Hello! Your favorite number is 23. Your favorite color is blue. Here is a moderately long list: (Your favorite color is now chartreuse.) 1. The Sacred Art of Dub 2. Only the Poor Man Feel It 3. Excitable Boy 4. Aki Special 5. Combat Rock 6. Talking Timbuktu 7. The Birth of the Cool
Several things are worth mentioning in our stylesheet. First, notice that when we defined values for the first two variables (favoriteNumber and favoriteColor), we had to quote the string "blue", but didn't have to quote 23. If we don't quote blue, the XSLT processor assumes we mean all the <blue> elements in the current context. We don't have to quote 23 because XML element names can't start with a number. It's a good idea to always quote literals, even those that can't be element names; chances are good that you'll forget this process at some point.
Also notice that we have two variables named favoriteColor. One is a global variable because its parent is the <xsl:stylesheet> element; the other is a local variable because it is defined in a <xsl:template>. When we access favoriteColor in the match="/" template, it has one value; when we access it inside the name="processListitems" template, it has another. Having two variables at the same level with the same name is an error. It's also an error to define an <xsl:variable> and an <xsl:param> with the same name at the same level.
Using an <xsl:choose> element to initialize an <xsl:variable> is a common technique. This technique is the equivalent of this procedural programming construct:
String complicatedVariable; if (count(listitems) > 10) complicatedVariable = "really long list"; else if (count(listitems)) > 5) complicatedVariable = "moderately long list"; else complicatedVariable = "fairly short list";
The last point we'll make is that a variable can be any of the XPath or XSLT variable types, including a node-set. When we call the processListitems template, the parameter we pass to it is a variable containing the node-set of all the <listitem> elements in our document. Inside the processListitems template, our variable (which is now technically a parameter) can be used inside an <xsl:for-each> element.
<xsl:when> | Defines one branch of an <xsl:choose> element. It is equivalent to the Java case statement. |
Subinstruction (<xsl:when> always appears as a child of an <xsl:choose> element)
None.
An XSLT template.
The <xsl:choose> element only.
XSLT section 9.2, Conditional Processing with xsl:choose.
This example uses an <xsl:choose> element and three <xsl:when> elements to cycle through a set of values. Now we will generate rows of an HTML table for each <listitem>:
<?xml version="1.0"?> <list xml:lang="en"> <title>Albums I've bought recently:</title> <listitem>The Sacred Art of Dub</listitem> <listitem>Only the Poor Man Feel It</listitem> <listitem>Excitable Boy</listitem> <listitem xml:lang="sw">Aki Special</listitem> <listitem xml:lang="en-gb">Combat Rock</listitem> <listitem xml:lang="zu">Talking Timbuktu</listitem> <listitem xml:lang="jz">The Birth of the Cool</listitem> </list>
In our stylesheet, we'll generate table rows with the background colors of mintcream, lavender, whitesmoke, and papayawhip. For each <listitem> in our source document, one of the <xsl:when> elements (or the <xsl:otherwise> element) generates the appropriate color.
<?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="html"/> <xsl:template match="/"> <html> <head> <title> <xsl:value-of select="list/title"/> </title> </head> <body> <h1><xsl:value-of select="list/title"/></h1> <table border="1"> <xsl:for-each select="list/listitem"> <tr> <td> <xsl:attribute name="bgcolor"> <xsl:choose> <xsl:when test="@bgcolor"> <xsl:value-of select="@bgcolor"/> </xsl:when> <xsl:when test="position() mod 4 = 0"> <xsl:text>papayawhip</xsl:text> </xsl:when> <xsl:when test="position() mod 4 = 1"> <xsl:text>mintcream</xsl:text> </xsl:when> <xsl:when test="position() mod 4 = 2"> <xsl:text>lavender</xsl:text> </xsl:when> <xsl:otherwise> <xsl:text>whitesmoke</xsl:text> </xsl:otherwise> </xsl:choose> </xsl:attribute> <xsl:value-of select="."/> </td> </tr> </xsl:for-each> </table> </body> </html> </xsl:template> </xsl:stylesheet>
When we process our XML source document with this stylesheet, here are the results:
<html> <head> <META http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Albums I've bought recently:</title> </head> <body> <h1>Albums I've bought recently:</h1> <table border="1"> <tr> <td bgcolor="mintcream">The Sacred Art of Dub</td> </tr> <tr> <td bgcolor="lavender">Only the Poor Man Feel It</td> </tr> <tr> <td bgcolor="whitesmoke">Excitable Boy</td> </tr> <tr> <td bgcolor="papayawhip">Aki Special</td> </tr> <tr> <td bgcolor="mintcream">Combat Rock</td> </tr> <tr> <td bgcolor="lavender">Talking Timbuktu</td> </tr> <tr> <td bgcolor="whitesmoke">The Birth of the Cool</td> </tr> </table> </body> </html>
All <td> elements with a background color of papayawhip, mintcream, or lavender were generated by one of the <xsl:when> elements.
<xsl:with-param> | Defines a parameter to be passed to a template. When the template is invoked, values can be passed in for the parameter. |
Subinstruction (<xsl:with-param> always appears inside an <xsl:apply-templates> or <xsl:call-template> element)
<xsl:with-param> defines a parameter to be passed to a template. When the template is invoked, values can be passed in for the parameter. The value of the parameter can be defined in one of three ways:
If the <xsl:with-param> element is empty and does not contain a select attribute, then no value is passed to the template.
If the <xsl:with-param> element is empty and has a select attribute, the value of the parameter is the value of the select attribute.
If the <xsl:with-param> element contains an XSLT template, the value of the parameter is the result of processing the template.
If no value is passed to the template (<xsl:with-param name="x"/>), then the default value of the parameter, if any, is used instead. The default value of the parameter is defined on the <xsl:param> element inside the <xsl:template> itself; see the description of the <xsl:param> element for more details.
The <xsl:with-param> element can be empty, or it can contain an XSLT template. If it contains an XSLT template, the value of the select attribute (if any exists) is ignored.
<xsl:apply-templates> and <xsl:call-template>.
XSLT section 11.6, Passing Parameters to Templates.
Here is a stylesheet with a number of parameters. Notice that some parameters are global and defined outside the stylesheet:
<?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="text"/> <xsl:variable name="newline"> <xsl:text> </xsl:text> </xsl:variable> <xsl:param name="favoriteNumber" select="23"/> <xsl:param name="favoriteColor"/> <xsl:template match="/"> <xsl:value-of select="$newline"/> <xsl:value-of select="list/title"/> <xsl:value-of select="$newline"/> <xsl:variable name="listitems" select="list/listitem"/> <xsl:call-template name="processListitems"> <xsl:with-param name="items" select="$listitems"/> <xsl:with-param name="color" select="'yellow'"/> <xsl:with-param name="number" select="$favoriteNumber"/> </xsl:call-template> </xsl:template> <xsl:template name="processListitems"> <xsl:param name="items"/> <xsl:param name="color" select="'blue'"/> <xsl:for-each select="$items"> <xsl:value-of select="position()"/> <xsl:text>. </xsl:text> <xsl:value-of select="."/> <xsl:value-of select="$newline"/> </xsl:for-each> <xsl:value-of select="$newline"/> <xsl:text>Your favorite color is </xsl:text> <xsl:value-of select="$favoriteColor"/> <xsl:text>.</xsl:text> <xsl:value-of select="$newline"/> <xsl:text>The color passed to this template is </xsl:text> <xsl:value-of select="$color"/> <xsl:text>.</xsl:text> <xsl:value-of select="$newline"/> </xsl:template> </xsl:stylesheet>
We'll use this stylesheet to transform this document:
<?xml version="1.0"?> <list xml:lang="en"> <title>Albums I've bought recently:</title> <listitem>The Sacred Art of Dub</listitem> <listitem>Only the Poor Man Feel It</listitem> <listitem>Excitable Boy</listitem> <listitem xml:lang="sw">Aki Special</listitem> <listitem xml:lang="en-gb">Combat Rock</listitem> <listitem xml:lang="zu">Talking Timbuktu</listitem> <listitem xml:lang="jz">The Birth of the Cool</listitem> </list>
Our stylesheet contains two global parameters, favoriteNumber and favoriteColor, and defines a default value for favoriteNumber. The stylesheet also passes a parameter from the match="/" template to the name="processListitems" template; that parameter contains a node-set. Here are the results of the transformation:
Albums I've bought recently: 1. The Sacred Art of Dub 2. Only the Poor Man Feel It 3. Excitable Boy 4. Aki Special 5. Combat Rock 6. Talking Timbuktu 7. The Birth of the Cool Your favorite color is orange. The color passed to this template is yellow.
To generate these results with Xalan, we use this command:
java org.apache.xalan.xslt.Process -in test4.xml -xsl with-param.xsl -param favoriteColor orange
The command should appear on a single line. See Section 4.4.3, "Global Parameters" in Chapter 4, "Branching and Control Elements" for a complete discussion of global parameters and how you define them for various XSLT processors.
Copyright © 2002 O'Reilly & Associates. All rights reserved.