XSLT in the mode for multiple usage

We are using XSLT and Saxon to generate an HTML file from an XML document.

Here the issue is that we want to process more than once a node from the XML in input. Can we do that? Yes, if we specify a "mode" attribute in the xsl:apply-templates and xsl:template elements.

As an example let's consider this XML, that represent a minimal version of the interesting XML book I'm reading while writing these posts:

<?xml version="1.0" encoding="UTF-8"?>
<Book>
<Title>Beginning XML, 4th Edition</Title>
<Authors>
<Author>David Hunter</Author>
<Author>Danny Ayers</Author>
<Author>al</Author>
</Authors>
<Year>2007</Year>
<Chapters>
<Chapter number="1" title="What is XML?">
XML is a markup language, derived from SGML.</Chapter>
<Chapter number="2" title="Well-formed XML">
To be well-formed an XML document must satisfy several rules about its
structure.</Chapter>
<Chapter number="3" title="Namespaces">
To help unambiguously identify the names of elements and attributes the
notion of an XML namespace is used.</Chapter>
<Chapter number="4" title="DTD">
A document type definition, DTD, is a way to specify the permitted
structure of an XML document.</Chapter>
<Chapter number="5" title="Schemas">
W3C XML Schema and Relax NG are two schema languages to specify the
structure of XML documents.</Chapter>
</Chapters>
</Book>

This is the HTML we would like to get from the input XML:

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Beginning XML, 4th Edition</title>
</head>
<body>
<h3>Beginning XML, 4th Edition</h3>
<p>by David Hunter, Danny Ayers, & al.</p>

<h3>Table of Contents</h3>
<p><b>1:</b>What is XML?</p>
<p><b>2:</b>Well-formed XML</p>
<p><b>3:</b>Namespaces</p>
<p><b>4:</b>DTD</p>
<p><b>5:</b>Schemas</p>

<h3>1. What is XML?</h3>
<p>XML is a markup language, derived from SGML.</p>

<h3>2. Well-formed XML</h3>
<p>To be well-formed an XML document must satisfy several
rules about its structure.</p>

<h3>3. Namespaces</h3>
<p>To help unambiguously identify the names of elements and
attributes the notion of an XML namespace is used.</p>

<h3>4. DTD</h3>
<p>A document type definition, DTD, is a way to specify
the permitted structure of an XML document.</p>

<h3>5. Schemas</h3>
<p>W3C XML Schema and Relax NG are two schema languages to
specify the structure of XML documents.</p>
</body>
</html>

The solution requires us to use an XSLT like this:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:template match="/">
<html>
<head>
<title><xsl:value-of select="/Book/Title"/></title>
</head>
<body>
<h3><xsl:value-of select="/Book/Title"/></h3>
<p>by <xsl:apply-templates select="/Book/Authors/Author"/></p> <!-- 1 -->
<h3>Table of Contents</h3>
<xsl:apply-templates select="/Book/Chapters/Chapter" mode="TOC"/> <!-- 2 -->
<xsl:apply-templates select="/Book/Chapters/Chapter" mode="full"/> <!-- 3 -->
</body>
</html>
</xsl:template>

<xsl:template match="Author">
<xsl:value-of select="."/>
<xsl:if test="position() != last()"> <!-- A -->
<xsl:text>, </xsl:text>
</xsl:if>
<xsl:if test="position() = last()-1"> <!-- B -->
<xsl:text>&amp; </xsl:text>
</xsl:if>
<xsl:if test="position() = last()"> <!-- C -->
<xsl:text>.</xsl:text>
</xsl:if>
</xsl:template>

<xsl:template match="Chapter" mode="TOC"> <!-- D -->
<p>
<b><xsl:value-of select="@number"/>:</b>
<xsl:value-of select="@title"/>
</p>
</xsl:template>

<xsl:template match="Chapter" mode="full"> <!-- E -->
<h3><xsl:value-of select="@number"/>. <xsl:value-of select="@title"/></h3>
<p><xsl:value-of select="."/></p>
</xsl:template>

</xsl:stylesheet>

The template matching the root element has three xsl:apply-templates elements:
1. It is not relevant for the mode attribute usage, but it is quite interesting for the usage of xsl:if elements combined with XPath() functions. In (A) we see that a comma-blank string is put in the output document if the current element passed to the template has position() different to the last(). In (B) we check if the current position() is last() -1, if so we put also an ampersand. And finally (C), if this is the last() element, we close the sentence with a full stop.
2. This apply-templates specifies a select and a mode tag, the template applied here would be the one matching both parameters: (D). This is the template for the Table Of Content that makes use of the number and title attribute of the passed Chapter element.
3. We use the same select attribute to choose the template, but this time the mode is "full", so we pick the (E) template up, in which also the child text for the current element is used.

More information on XSLT and Saxon in chapter eight of Beginning XML by David Hunter et al. (Wrox).

No comments:

Post a Comment