In our last
article,
we touched the surface of Ajax by developing a simple email validation application. In this
article we are going to delve deeper into Ajax and explore how
XSL can be used on both the client side (using Javascript) and on the server
(using PHP) to transform XML data into XHTML.
XSL is an application of
XML that is used to describe the presentation of XML through the use of
stylesheets. It is similar to CSS, however, it is generic enough to be
applied to any type of XML data.
Although originally created to format XML documents for visual display,
XSL has found another niche: XSL-Transformations (XSLT). XSLT is used
to transform an XML document into another XML document. It can make
subtle changes to the document, or it can completely rearrange the
order and structure of the data. One of the common uses of XSLT is to
convert XML to XHTML or HTML.
XSL uses the xPath language (introduced in my previous article) to
navigate through the XML document. The templates, iterative
loops and conditionals all use xPath expressions to select the
necessary data.
Namespaces allow two different
types of XML to co-exist in the same document. As XSL is a an
application of XML which is used to transform different XML
formats, it is quite probable that the stylesheet will contain more
than one type of XML. Therefore, XSL uses the its own namespace. A
namespace can be declared in any element and affects all its children.
In XSL, it is declared in the root (<xsl:stylesheet>)
element.
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
</xsl:stylesheet>
The namespace declaration takes the following format:
xmlns:ns="uri"
ns = the
namespace identifier. Any element name prefixed with
ns will fall into
this namespace.
uri = this
is the namespace URI. It can be anything you like as long as it is
unique within the context of the document. Authors often use this to
point to information about the XML format on the Internet.
For more information about XML namepaces, you can find the full
documentation on the
w3c website. All XSL tags must be prefixed with the xsl namespace prefix, otherwise they will be ignored.
To demonstrate XSLT, we first
need an XML file to use as input. We will be using the library XML
example seen in the first part of the series. A few modifications,
however, have been made to the book definitions. Among these, we have
added the ISBN number as an attribute to uniquely identify each book
and we have separated the authors into their own separate list.
<library>
<categories>
<category id="1">Category
Name</category>
</categories>
<authors>
<author id="1">Author
Name</author>
</authors>
<book isbn="0471777781"
hascover="yes">
<title>Professional Ajax</title>
<publisher>Wrox</publisher>
<category>3</category>
<category>6</category>
<author>7</author>
<author>8</author>
<author>9</author>
<synopsis>
Combining tried-and-true CSS, XML and
Javascript technologies, Ajax provides web developers with the
ability to create more sophisticated and
responsive user interfaces and break free from the
"click-and-wait" standard that has dominated
the web since its introduction.
</synopsis>
</book>
</library>
The full XML file can be found in the
ZIP file
accompanying this article.
This XSL stylesheet when applied
to the library XML, transforms it to XHTML.
XSL:
<?xml
version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- the output
method can be
text | xml | html !-->
<xsl:output method="html"
omit-xml-declaration="yes" encoding="UTF-8"
doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"
doctype-public="-//W3C//DTD XHTML 1.0
Strict//EN"
/>
<!-- start with
matching the root
of the XML -->
<xsl:template
match="/library">
<html>
<head>
<title>XML Library</title>
</head>
<body>
<!-- the
apply-templates attribute applies all other XSL
templates to direct descendants of the currently
selected node. Some XSL processors spit out text if no template exists
for a given descendant, therefore
an xPath expression is used to select only the nodes we want transformed -->
<xsl:apply-templates select="books/book">
<!-- the
XSL to match
each book element is held in a separate template
purely to aid in readability of the stylesheet !-->
<xsl:sort select="title"
/> <!--
sort the list by book title -->
</xsl:apply-templates>
</body>
</html>
</xsl:template>
<xsl:template match="book">
<!--
variables are used as they can be embedded inside xPath
expressions and HTML attributes -->
<xsl:variable name="isbn" select="@isbn"
/>
<xsl:variable name="title" select="title"
/>
<xsl:variable name="hascover" select="@hascover"
/>
<div>
<hr />
<!-- here we extract
the value of
a single element which contains a single text node -->
<h2><xsl:value-of
select="$title"
/></h2>
<hr />
<!-- below, an
xsl:if test
ensures we only display the cover if the havcover attribute of the book
tag is set to yes -->
<xsl:if test="$hascover =
'yes'">
<img
alt="{$title}" src="../book_covers/{$isbn}.jpg" />
</xsl:if>
<p><xsl:value-of
select="synopsis"
/></p>
<h3>Categories</h3>
<ul>
<!-- we
could have use for-each to similar to the one used below
to iterate through the book
elements instead of a template
-->
<xsl:for-each select="category">
<!-- in XSL, we use
"." to
extract the value of the current element and store its value
in a variable called cid -->
<xsl:variable name="cid"
select="." />
<!-- the xPath
expression here
extracts the category name from the category list
based on the value stored in
the cid variable -->
<li><xsl:value-of
select="/library/categories/category[@id=$cid]"
/></li>
</xsl:for-each>
</ul>
<h3>Authors</h3>
<ul>
<xsl:for-each select="author">
<xsl:variable name="aid"
select="."
/>
<li><xsl:value-of
select="/library/authors/author[@id=$aid]"
/></li>
</xsl:for-each>
</ul>
<p><b>Publisher:</b>
<xsl:value-of select="publisher"
/></p>
<p><b>ISBN:
</b> <xsl:value-of select="$isbn"
/></p>
</div>
</xsl:template>
</xsl:stylesheet>
Although the structure of XSL is quite self-explanatory,
here are a few important points to observe:
- Notice the doctype-system and doctype-public attributes in
the xsl:output element. This ensures the output is valid XHTML.
- The xPath expressions which appear in select, match and
test
attributes are evaluated in the context of the currently selected
element. Therefore the following expression takes the value of the
current category element selected, within the xsl:for-each iteration.
<xsl:variable
name="cid" select="." />
- XSL variables are used where the value is to be included as
part of the xPath expression or inside an HTML attribute. The declaration takes
the following form:
<xsl:variable
name="title" select="title"
/>
Inside xPath expressions, variables are prepended with a dollar:
<h2><xsl:value-of
select="$title"
/></h2>
Inside XML/HTML attributes they are additionally enclosed in curly
brackets:
<img
alt="{$title}"
src="book_covers/{$isbn}.jpg" />
- The first xsl template must contain an xPath expression that matches relative to the root of the XML document element <library>
- The xsl:apply-templates element matches only books elements. This ensures that the transformer does not attempt to transform the category and author lists.
Having created a style sheet, applying it to the XML does not require any code. Internet Explorer 6 and Firefox can both transform an XML document which contains an xml-stylesheet declaration:
<?xml
version="1.0"?>
<?xml-stylesheet type="text/xsl" href="book_summary.xsl"
?>
<library>