Template Languages in XSLT
by Jason Diamond
March 27, 2002
Introduction
Despite its simplicity and its original purpose, XSLT is an extremely rich
and powerful programming language. Just
about anything that can be done with XML can be implemented in XSLT -- all it
really takes is a little bit of creativity and a whole lot of pointy
brackets.
One of the most common uses of XSLT is to transform XML content into
something more suitable for viewing. This separation between content and
presentation seems to be the most often cited advantage for many XML
advocates. XSLT was designed specifically for this task
It could be argued, however, that, XSLT fails miserably at separating
these two layers. Traversing source documents with any sort of XPath or XSLT
instructions like xsl:for-each and
xsl:apply-templates in your style sheets is like opening a
connection to a database and performing a query in the middle of an ASP or
JSP page. Good programmers don't do this because it breaks the separation
between the presentation and data tiers in their applications.
Thinking about it from an altogether different perspective, having literal
result elements interspersed with XSLT instructions in your transforms is
like generating HTML by concatenating strings and then printing them to your
output (as is often done when implementing servlets). Most designers can't
work in an environment like that. Even if they can, they shouldn't have to
concern themselves with all the logic of extracting and manipulating the data
they're trying to present.
Table of
Contents
- Introduction
- Literal
Result Elements
- Instructions
- Simple
Conditionals (if)
- Loops (for-each)
- Advanced
Conditionals (choose/when/otherwise)
- Instruction
Parameters (attributes)
- Attribute
Value Templates
- Conclusion
- Bibliography
No matter how you look at, you've already lost the separation you've been
trying so hard to retain. If your style sheets are coupled to the structure
of your source documents, even slight modifications to your vocabulary could
require updates to each and every style sheet that operates on those
documents.
Eric van der Vlist introduced the concept of "Style-free
XSLT Style Sheets" in November, 2000. That article served as the
inspiration for implementing my own XSLT-like template language (in XSLT)
designed specifically for transforming instances of a particular vocabulary
into whatever output format a designer could dream up without requiring any knowledge of XPath,
XSLT, or the XML vocabulary they were transforming. This language included
all of the traditional control flow constructs necessary to generate the
desired output.
This article will show you how to implement your own specialized template
languages by building up a simple example capable of transforming a music
database in XML into any form of HTML.
Example 1. A sample source document (collection.xml)
<collection>
<owner>
<name><given>Jason</given>
<family>Diamond</family></name>
(<email>jason@injektilo.org</email>)
</owner>
<album>
<artist>Radiohead</artist>
<title>OK Computer</title>
</album>
<album>
<artist>Sigur Rós</artist>
<title>Ágætis Byrjun</title>
</album>
<album>
<artist>Mogwai</artist>
<title>Kicking a Dead Pig</title>
</album>
</collection>
All of the files referenced in this article can be found in the associated
archive. Included
in that archive is a slightly more practical example of using the same
template to process two different versions of RSS.
Literal Result Elements
Like XSLT, templates in our template language will be well-formed XML
documents containing both literal result elements and instructions. Handling
literal result elements is easy -- just copy them to the result tree.
Example 2. Our simple identity transform (transform1.xslt)
<xsl:transform version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:param name="template-uri" />
<xsl:variable name="template"
select="document($template-uri)" />
<xsl:variable name="source" select="/" />
<xsl:template match="/">
<xsl:apply-templates
select="$template/node()" />
</xsl:template>
<xsl:template match="*">
<xsl:element name="{name()}">
<xsl:apply-templates
select="@* | node()" />
</xsl:element>
</xsl:template>
<xsl:template match="@*">
<xsl:attribute name="{name()}">
<xsl:value-of select="." />
</xsl:attribute>
</xsl:template>
<xsl:template match="text()">
<xsl:if test="normalize-space()">
<xsl:value-of select="." />
</xsl:if>
</xsl:template>
</xsl:transform>
There's a couple of things to note about this example. First, this
transform requires a parameter named template-uri. This
URI is used as the input to the document function to retrieve
the actual contents of our template. We start the transform off by processing
the nodes in the template document and not the source document (although we do save
the root of the source document in the source variable so that
we can access it later).
Related Reading
XSLT
By DougTidwell
Table of Contents
Index
Sample Chapter
Author's Article
Read Online--Safari
With just a few small variations, the transform almost looks like the
identity transform. We're using the xsl:element and
xsl:attribute instructions instead of xsl:copy
since xsl:copy automatically copies the namespace nodes of the
current node which would include the template language namespace declaration.
We're also only copying text nodes over when they don't consist of just
whitespace, purely for aesthetic reasons.
In XSLT templates, literal result elements are the elements that do not
belong to the XSLT namespace. Assuming the template language namespace name
is http://xml.com/my-template-language namespace, the following
template will produce an identical result document (minus the namespace
declaration).
Example 3. A useless template (template1.xml)
<html xmlns:my="http://xml.com/my-template-language">
<body>
<h1>My Collection</h1>
</body>
</html>
Our template language is, so far, worthless. That's about to change with
the addition of our first instruction.
[1] [2] [3] Next