Common XSLT Tasks Ц Part I (Transformation and Sorting)
|
Abstract:
|
XSLT is a W3C specification (www.w3.org/TR/xslt),
a declarative language, designed to transform a well-formed XML document into any other
text format, such as XML, HTML, WML, SVG, and so on.
XSLT supports the notion of "separating content from the presentation". Once content/data is available in
XML document, various XSLT stylesheets can be used with the same XML document,
to generate different outputs. XSLT processor then can be used to apply the
XSLT stylesheet on the XML document. Popular XSLT processors include MSXML from Microsoft,
Saxon by Michael Kay, Xalan by Apache, and Oracle XSLT processor.
In this five-part article series, we'll present examples that illustrate some common XSLT tasks including:
- transforming XML,
- sorting,
- generating nodes,
- conditions and looping,
- recursion,
- string, number, and date manipulation,
- including and importing stylesheets, and
- XSLT extensions.
In this first part, we'll see examples of transforming XML and sorting.
Examples in this first part:
|
|
Author:
|
perfectxml.com Team
|
|
Last Updated:
|
February 22, 2003
|
|
Download:
|
XSLTPart1Ex.zip (10 KB ZIP file)
|
Transformation
The XML 1.0 specification turned five on Feb 10, 2003. On this day, Dave Hollander and C. M. Sperberg-McQueen (members of the W3C Working Group which developed the XML 1.0 specification) published an article on the W3C site. Here are some of the notable quotes from this article:
"XML has become pervasive nearly everywhere that information is managed".
"XML has changed not only the way people publish documents on the Web but also the way people manage information internal to their enterprise."
"Before we knew it, all sorts of people started using XML Ч best of all, doing so without the permission or guidance of the Working Group. Database people, transaction designers, system engineers, B2B developers all crashed our party. Why, an outsider even got an article on XML published in Time magazine!"
"Remarkably, XML became pervasive nearly everywhere that text-based information is managed by computers!"
XML is being used in various applications for data-interchange, messaging, B2B and application integration.
Other interesting areas where XML is being used includes voice, wireless, and multi-modal applications (WML, WAP, SALT, VoiceXML),
vector graphics (SVG), configuration files (ASP.NET config files), content management, and so on.
In my opinion, the three primary reasons behind XML's success are its textual nature, the tools support,
and the specifications surrounding XML.
XSLT is one such W3C specification, that itself is based on XML, and is highly used in XML applications.
The primary XSLT design goal is to allow transforming the source XML tree into any other text format.
Let's say you have some data available in the XML format, you can now write the
XSLT stylesheets using the declarative syntax, describing how you want the outputs to look, and
then XSLT processor can be used to pass the same XML document, but different stylesheets, to produce different outputs.
In this section, we'll show you four XSLT stylesheet examples that illustrate transforming XML document into HTML, text, XML, and SVG.
Let's begin with an example of XSLT stylesheet used to transform XML into HTML.
XML to HTML
The goal is to transform Invoice XML document into the HTML format.
Here is the source XML file (Invoice.xml):
<?xml version='1.0' encoding='UTF-8'?>
<Invoice>
<PO>38106</PO>
<PODate>12/2/1999</PODate>
<Invoice>83882466</Invoice>
<InvoiceDate>12/3/1999</InvoiceDate>
<ShipDate>12/3/1999</ShipDate>
<TotalAmount>1290.80</TotalAmount>
<LineItem>
<Item>1</Item>
<Qty units='Each'>2.0</Qty>
<UnitPrice>605.30</UnitPrice>
<Desc>256MB Compaq DeskPro</Desc>
</LineItem>
<LineItem>
<Item>2</Item>
<Qty units='Each'>4.0</Qty>
<UnitPrice>20.050</UnitPrice>
<Desc>Keyboard</Desc>
</LineItem>
<ShipToAddress>
<Addressee>Acme Exploration</Addressee>
<Street>23 Congress Ave.</Street>
<Suite>RM 377</Suite>
<City>Austin</City>
<State>TX</State>
<Zip>78703</Zip>
</ShipToAddress>
</Invoice>
And here is the XSLT stylesheet used to transform the above XML into HTML:
(Invoice.xsl):
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" />
<xsl:template match="/">
<html>
<head>
<title>Invoice</title>
<link rel="stylesheet" href="Invoice.css" />
</head>
<body>
<h1>Invoice</h1>
<b><u>Ship to:</u></b><br />
<xsl:value-of select="/Invoice/ShipToAddress/Addressee" /><br />
<xsl:value-of
select="concat(/Invoice/ShipToAddress/Street, ', ',/Invoice/ShipToAddress/Suite)" />
<br />
<xsl:value-of
select="concat(/Invoice/ShipToAddress/City, ' ',/Invoice/ShipToAddress/State, ' ', /Invoice/ShipToAddress/Zip)" />
<br />
<table class="itemTable" cellspacing="0" cellpadding="0">
<thead class="tblHeader">
<tr>
<td>PO Number</td>
<td>PO Date</td>
<td>Invoice Number</td>
<td>Invoice Date</td>
<td>Ship Date</td>
</tr>
</thead>
<tr class="itemCell">
<td><xsl:value-of select="/Invoice/PO"/></td>
<td><xsl:value-of select="/Invoice/PODate"/></td>
<td><xsl:value-of select="/Invoice/Invoice"/></td>
<td><xsl:value-of select="/Invoice/InvoiceDate"/></td>
<td><xsl:value-of select="/Invoice/ShipDate"/></td>
</tr>
</table>
<h2>Particulars</h2>
<table class="itemTable" cellspacing="0" cellpadding="0">
<thead class="tblHeader">
<tr>
<td>Item</td>
<td>Quantity</td>
<td>Unit Price</td>
<td>Net Price</td>
<td>Description</td>
</tr>
</thead>
<xsl:for-each select="/Invoice/LineItem">
<tr class="itemCell">
<td><xsl:value-of select="Item"/></td>
<td><xsl:value-of select="Qty"/></td>
<td>$<xsl:value-of select="format-number(UnitPrice,'#.00')"/>
<xsl:text> </xsl:text><xsl:value-of select="Qty/@units"/></td>
<td >$<xsl:value-of select="format-number(UnitPrice*Qty,'#.00')"/></td>
<td><xsl:value-of select="Desc"/></td>
</tr>
</xsl:for-each>
<tfoot class="tblHeader">
<tr>
<td colspan="3">TOTAL AMOUNT: </td>
<td colspan="2">$<xsl:value-of select="format-number(/Invoice/TotalAmount, '#.00')"/></td>
</tr>
</tfoot>
</table>
<hr size="1" color="#443300" />
</body>
</html>
</xsl:template>
</xsl:stylesheet>
When the above XSLT stylesheet is applied on the source invoice XML document, the generated HTML output, when viewed in the browser, looks similar to shown in the following screenshot:
Source Files:
The Invoice.xsl XSLT stylesheet above uses the <xsl:output method="html" /> instruction to tell the processor to format the output as HTML. Next
the root / matching template begins creating the HTML output, and finally a table row is created for each invoice line item.
| XSLT elements illustrated in this example |
Functions illustrated in this example |
|
|
|
XML to Text
Some people think that XSLT is used only to generate HTML from the XML document. However, the fact is that XSLT stylesheet can be used to transform well-formed XML into any other text format, and not necessarily only HTML or well-formed XML. In this example, you'll learn how XSLT can be used to transform XML into plain text format.
We'll take a configuration XML sample source file and transform into an INI file format.
Here is the source XML file (Config.xml):
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.web>
<compilation defaultLanguage="vb" debug="true" />
<customErrors mode="RemoteOnly" />
<authorization>
<allow users="*" />
<deny roles="WebUsers" />
</authorization>
<trace enabled="false" requestLimit="10" pageOutput="false" traceMode="SortByTime" localOnly="true" />
<globalization requestEncoding="utf-8" responseEncoding="utf-8" />
</system.web>
</configuration>
The following stylesheet is used to transform the above config XML file into an INI file:
(ConfigINI.xsl):
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" indent="no" />
<xsl:strip-space elements="*" />
<xsl:variable name="xslNewLine">
<xsl:text>
</xsl:text>
</xsl:variable>
<xsl:template match="/">
<xsl:call-template name="attribConfig">
<xsl:with-param name="elemXPath" select="//compilation" />
<xsl:with-param name="sectionName" select="string('CompileOptions')" />
</xsl:call-template>
<xsl:apply-templates />
<xsl:call-template name="attribConfig">
<xsl:with-param name="elemXPath" select="//trace" />
<xsl:with-param name="sectionName" select="string('Trace')" />
</xsl:call-template>
<xsl:call-template name="attribConfig">
<xsl:with-param name="elemXPath" select="//globalization" />
<xsl:with-param name="sectionName" select="string('Globalization')" />
</xsl:call-template>
</xsl:template>
<!--generic template (for elements such as compilation, trace, globalization, etc. -->
<xsl:template name="attribConfig">
<xsl:param name="elemXPath" />
<xsl:param name="sectionName"/>
<xsl:value-of select="$xslNewLine" />
<xsl:text>[</xsl:text><xsl:value-of select="$sectionName"/><xsl:text>]</xsl:text>
<xsl:value-of select="$xslNewLine" />
<xsl:for-each select="$elemXPath/@*">
<xsl:value-of select="name()" /><xsl:text>=</xsl:text><xsl:value-of
select="." /><xsl:text />
<xsl:value-of select="$xslNewLine" />
</xsl:for-each>
</xsl:template>
<!--customErrors-->
<xsl:template match="customErrors">
<xsl:value-of select="$xslNewLine" />
<xsl:text>[Errors]</xsl:text><xsl:value-of select="$xslNewLine" />
<xsl:text>Mode=</xsl:text><xsl:value-of select="@mode" />
<xsl:value-of select="$xslNewLine" />
</xsl:template>
<!--authorization-->
<xsl:template match="authorization">
<xsl:value-of select="$xslNewLine" />
<xsl:text>[Security]</xsl:text><xsl:value-of select="$xslNewLine" />
<xsl:text>Valid=</xsl:text><xsl:value-of
select="concat(allow/@users,',',allow/@roles,',',allow/@verbs)" />
<xsl:value-of select="$xslNewLine" />
<xsl:text>InValid=</xsl:text><xsl:value-of
select="concat(deny/@users,',',deny/@roles,',',deny/@verbs)" />
<xsl:value-of select="$xslNewLine" />
</xsl:template>
</xsl:stylesheet>
When the above stylesheet is applied on the source config XML file, it produces the following output:
[CompileOptions]
defaultLanguage=vb
debug=true
[Errors]
Mode=RemoteOnly
[Security]
Valid=*,,
InValid=,WebUsers,
[Trace]
enabled=false
requestLimit=10
pageOutput=false
traceMode=SortByTime
localOnly=true
[Globalization]
requestEncoding=utf-8
responseEncoding=utf-8
Source Files:
Some of the tags in the sample config XML file have all the configuration details as attribute values.
compilation, trace, and globalization are
the examples of such tags. For these tags, the stylesheet includes a generic routine,
a named template (called attribConfig), which loops over
any and all attributes on the node selected by using the provided XPath expression
parameter (elemXPath), and creates a new INI section and keys under
it. For other tags (such as customErrors, authorization, etc.) in
the source config XML file, the stylesheet defines how to generate the INI sections and the
keys under it using the xsl:text instruction.
| XSLT elements illustrated in this example |
Functions illustrated in this example |
|
|
|
XML to XML
The next example shows how to use XSLT to transform source XML tree into a different XML tree.
Instead of changing the structure or content, we'll just change all the element and attribute names
from lowercase to the uppercase.
Let's use the same Config.xml file as the source
XML file, and here is the XSLT stylesheet that converts all element and attribute names to uppercase
(ToUpper.xsl):
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="no" encoding="UTF-8" version="1.0" />
<xsl:template match="/">
<xsl:apply-templates />
</xsl:template>
<xsl:template match="*">
<xsl:variable name="varElemName"
select="translate(name(), 'abcdefghijklmnopqrstuvwxyz','ABCDEFGHIJKLMNOPQRSTUVWXYZ')" />
<xsl:element name="{$varElemName}">
<xsl:for-each select="@*">
<xsl:variable name="varAttribName"
select="translate(name(), 'abcdefghijklmnopqrstuvwxyz','ABCDEFGHIJKLMNOPQRSTUVWXYZ')" />
<xsl:attribute name="{$varAttribName}"><xsl:value-of select="."/></xsl:attribute>
</xsl:for-each>
<xsl:apply-templates />
</xsl:element>
</xsl:template>
</xsl:stylesheet>
The above stylesheet contains the template <xsl:template match="*"> - which
matches all the element nodes. So, for each element, the element name is converted from lowercase to
uppercase using the translate function. This name is stored in a
variable (varElemName). This uppercase name is then used to create a new element in the result XML
using <xsl:element> instruction.
Next, <xsl:for-each select="@*"> matches all the attributes on the
current element, and for each attribute, we once again use the combination of variable declaration, the
translate function, and this time the
<xsl:attribute> instruction to create a new attribute with name in
uppercase.
Source Files:
| XSLT elements illustrated in this example |
Functions illustrated in this example |
|
|
|
XML to SVG
Scalable Vector Graphics or SVG, yet another application of XML, is a W3C specification designed to create
two-dimensional graphics using XML. The animation support in SVG is added via DOM-based scripting.
SVG 1.1 received the W3C recommendation status
last month (Jan 2003), and SVG 1.2 is currently in
the Working Draft status.
In addition to vector graphics shapes, SVG also supports other graphics images and text.
I think the best way to learn SVG is to
read the SVG specification and understand the
SVG tags, attributes, and their meanings, write a sample SVG file,
download and install Adobe SVG Viewer 3.0, and refer to
the SVG file using the object in an HTML file, or save the SVG text into a .svg file and
open that file in Internet Explorer.
Here are some links where you can learn more about SVG:
Books on SVG:
The source XML file in this example contains the sales data for year 2002 for all the four regions, north, east, west and south.
We'll write a XSLT stylesheet that transforms this sales data XML into SVG file,
that when viewed in the browser using Adobe SVG Viewer,
shows a bar chart for that sales data. To see the bar chart output, download
and install SVG Viewer from the
Adobe Web site.
Here is the source XML file (Sales2002.xml)
<?xml version="1.0" encoding="utf-8" ?>
<Sales>
<Report Year="2002">
<Summary Region="North">
<Q1 TotalSales="130" />
<Q2 TotalSales="150" />
<Q3 TotalSales="160" />
<Q4 TotalSales="180" />
</Summary>
<Summary Region="East">
<Q1 TotalSales="110" />
<Q2 TotalSales="90" />
<Q3 TotalSales="100" />
<Q4 TotalSales="130" />
</Summary>
<Summary Region="West">
<Q1 TotalSales="160" />
<Q2 TotalSales="170" />
<Q3 TotalSales="150" />
<Q4 TotalSales="190" />
</Summary>
<Summary Region="South">
<Q1 TotalSales="110" />
<Q2 TotalSales="100" />
<Q3 TotalSales="90" />
<Q4 TotalSales="110" />
</Summary>
</Report>
</Sales>
And here is the XSLT stylesheet used to transform the above XML into SVG:
(BarChart.xsl)
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" media-type="image/svg+xml" indent="yes" />
<xsl:strip-space elements="*" />
<xsl:template match="/">
<svg width="14cm" height="10cm" xmlns="http://www.w3.org/2000/svg">
<!--Heading-->
<text x="5" y="28" text-anchor="left" font-weight="bolder" font-size="20" fill="maroon" text-decoration="underline">
Sales Report for ACME Sample Corp.
Year <xsl:value-of select="/Sales/Report/@Year"/>
</text>
<!--Caption (Vertical)-->
<g transform="translate(40, 100) rotate(270, 0, 0)">
<text x="-98" y="5" text-anchor="middle" font-weight="bolder" font-size="16" fill="black">
Sales (Hundred Thousand USD)
</text>
</g>
<!--Caption (Horizontal)-->
<text x="190" y="350" font-size="16" font-weight="bolder" fill="black">
Region (Per Quarter)
</text>
<!--Lines-->
<g stroke="gray" stroke-width="1">
<line x1="70" y1="300" x2="490" y2="300"/>
<line x1="70" y1="300" x2="70" y2="70"/>
</g>
<!--Data Charts-->
<xsl:call-template name="drawSalesBars">
<xsl:with-param name="Region" select="string('North')"/>
<xsl:with-param name="barColor" select="string('#B8CCDE')"/>
<xsl:with-param name="startXPosition" select="80"/>
</xsl:call-template>
<xsl:call-template name="drawSalesBars">
<xsl:with-param name="Region" select="string('East')"/>
<xsl:with-param name="barColor" select="string('#FFFFCC')"/>
<xsl:with-param name="startXPosition" select="180"/>
</xsl:call-template>
<xsl:call-template name="drawSalesBars">
<xsl:with-param name="Region" select="string('West')"/>
<xsl:with-param name="barColor" select="string('#9999FF')"/>
<xsl:with-param name="startXPosition" select="280"/>
</xsl:call-template>
<xsl:call-template name="drawSalesBars">
<xsl:with-param name="Region" select="string('South')"/>
<xsl:with-param name="barColor" select="string('#993366')"/>
<xsl:with-param name="startXPosition" select="380"/>
</xsl:call-template>
</svg>
</xsl:template>
<xsl:template name="drawSalesBars" xmlns="http://www.w3.org/2000/svg">
<xsl:param name="Region"/>
<xsl:param name="barColor"/>
<xsl:param name="startXPosition"/>
<text x="{$startXPosition+10}" y="320" font-weight="bolder" font-size="16" fill="#225C91">
<xsl:value-of select="$Region" />
</text>
<xsl:variable name="q1SalesAmt" select="/Sales/Report/Summary[@Region=$Region]/Q1/@TotalSales" />
<xsl:variable name="q1YPos" select="300 - $q1SalesAmt" />
<rect stroke-width="1" stroke="black" width="15"
fill="{$barColor}" x="{$startXPosition}" y="{$q1YPos}" height="{$q1SalesAmt}"/>
<text x="{$startXPosition}" y="{$q1YPos - 5}" font-size="11" fill="black"><xsl:value-of select="$q1SalesAmt"/></text>
<xsl:variable name="q2SalesAmt" select="/Sales/Report/Summary[@Region=$Region]/Q2/@TotalSales" />
<xsl:variable name="q2YPos" select="300 - $q2SalesAmt" />
<rect stroke-width="1" stroke="black" width="15"
fill="{$barColor}" x="{$startXPosition + 20}" y="{$q2YPos}" height="{$q2SalesAmt}"/>
<text x="{$startXPosition + 20}" y="{$q2YPos - 5}" font-size="11" fill="black"><xsl:value-of select="$q2SalesAmt"/></text>
<xsl:variable name="q3SalesAmt" select="/Sales/Report/Summary[@Region=$Region]/Q3/@TotalSales" />
<xsl:variable name="q3YPos" select="300 - $q3SalesAmt" />
<rect stroke-width="1" stroke="black" width="15"
fill="{$barColor}" x="{$startXPosition + 40}" y="{$q3YPos}" height="{$q3SalesAmt}"/>
<text x="{$startXPosition + 40}" y="{$q3YPos - 5}" font-size="11" fill="black"><xsl:value-of select="$q3SalesAmt"/></text>
<xsl:variable name="q4SalesAmt" select="/Sales/Report/Summary[@Region=$Region]/Q4/@TotalSales" />
<xsl:variable name="q4YPos" select="300 - $q4SalesAmt" />
<rect stroke-width="1" stroke="black" width="15"
fill="{$barColor}" x="{$startXPosition + 60}" y="{$q4YPos}" height="{$q4SalesAmt}"/>
<text x="{$startXPosition + 60}" y="{$q4YPos - 5}" font-size="11" fill="black"><xsl:value-of select="$q4SalesAmt"/></text>
</xsl:template>
</xsl:stylesheet>
When the above XSLT stylesheet is applied on the sales data XML, the transformed SVG when viewed using Adobe SVG Viewer looks similar to the following screenshot:
Source Files:
SVG document is essentially an XML document, and hence the XSLT stylesheet above defines the output method as XML. Next, the
stylesheet starts creating the SVG text, including the headings, lines, and for each region,
a generic template named drawSalesBars is called by passing the three parameters, the
region name, the bar rectangle fill color, and starting X position. This named
template then gets the values from the sales XML file, does some computations, and draws rectangles and labels.
| XSLT elements illustrated in this example |
|
|
Sorting
Let's conclude this first part with an example of sorting the data while applying the XSLT stylesheet.
In addition to the string sorting, this example also illustrates sorting based on the numeric and date values.
The source XML file (BestBooks.xml) in this example contains information on best selling XSLT books; and the XSLT
stylesheet (BestBooks.xsl) file transforms this XML into HTML.
The generated output is sorted based on the parameter passed to the XSLT stylesheet.
See this sorting example demo live!
Here is the source XML file (BestBooks.xml):
<?xml version="1.0" encoding="utf-8" ?>
<AmazonBestSelling catagory="XSLT">
<Book ISBN="0201740958">
<Title>Essential XML Quick Reference</Title>
<Rank>1</Rank>
<Author>Aaron Skonnard</Author>
<PublishDate>11/23/2001</PublishDate>
<Price>29.99</Price>
</Book>
<Book ISBN="1861005067">
<Title>XSLT Programmer's Reference 2nd Edition</Title>
<Rank>2</Rank>
<Author>Michael H. Kay</Author>
<PublishDate>04/01/2001</PublishDate>
<Price>34.99</Price>
</Book>
<Book ISBN="0596003722">
<Title>XSLT Cookbook</Title>
<Rank>3</Rank>
<Author>Sal Mangano</Author>
<PublishDate>12/01/2002</PublishDate>
<Price>39.95</Price>
</Book>
<Book ISBN="0596000537">
<Title>XSLT</Title>
<Rank>4</Rank>
<Author>Doug Tidwell</Author>
<PublishDate>08/15/2001</PublishDate>
<Price>39.95</Price>
</Book>
<Book ISBN="1861005946">
<Title>Beginning XSLT</Title>
<Rank>5</Rank>
<Author>Jeni Tennison</Author>
<PublishDate>05/01/2002</PublishDate>
<Price>39.99</Price>
</Book>
</AmazonBestSelling>
And here is the XSLT Stylesheet file (BestBooks.xsl):
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" indent="no" />
<xsl:strip-space elements="*" />
<xsl:param name="SortBy" select="'Rank'" />
<xsl:variable name="IsRank">
<xsl:if test="$SortBy = 'Rank'">
(*)
</xsl:if>
</xsl:variable>
<xsl:variable name="IsTitle">
<xsl:if test="$SortBy = 'Title'">
(*)
</xsl:if>
</xsl:variable>
<xsl:variable name="IsAuthor">
<xsl:if test="$SortBy = 'Author'">
(*)
</xsl:if>
</xsl:variable>
<xsl:variable name="IsPDate">
<xsl:if test="$SortBy = 'PublishDate'">
(*)
</xsl:if>
</xsl:variable>
<xsl:variable name="IsPrice">
<xsl:if test="$SortBy = 'Price'">
(*)
</xsl:if>
</xsl:variable>
<xsl:template match="/">
<xsl:variable name="caption">
Amazon.com Best Selling <xsl:value-of select="/AmazonBestSelling/@catagory"/> Books
</xsl:variable>
<html>
<head>
<title><xsl:value-of select="$caption" /></title>
<link rel="stylesheet" href="Books.css" />
</head>
<body>
<h1><xsl:value-of select="$caption" /></h1>
<table class="bookTable" cellspacing="0" cellpadding="0">
<thead class="tblHeader">
<tr>
<td><a href="xsltbooks.asp?s=Rank">Rank</a> <xsl:value-of select="$IsRank" /></td>
<td><a href="xsltbooks.asp?s=Title">Title</a> <xsl:value-of select="$IsTitle" /></td>
<td><a href="xsltbooks.asp?s=Author">Author</a> <xsl:value-of select="$IsAuthor" /></td>
<td><a href="xsltbooks.asp?s=PublishDate">Publish Date</a> <xsl:value-of select="$IsPDate" /></td>
<td><a href="xsltbooks.asp?s=Price">Price</a> <xsl:value-of select="$IsPrice" /></td>
</tr>
</thead>
<xsl:choose>
<xsl:when test="$SortBy = 'PublishDate'">
<xsl:apply-templates mode="dateSort" />
</xsl:when>
<xsl:when test="$SortBy = 'Price' or $SortBy = 'Rank'">
<xsl:apply-templates mode="numericSort" />
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates mode="regularSort" />
</xsl:otherwise>
</xsl:choose>
</table>
</body>
</html>
</xsl:template>
<xsl:template match="AmazonBestSelling" mode="regularSort">
<xsl:for-each select="Book">
<xsl:sort select="*[name() = $SortBy]" data-type="text"/>
<xsl:call-template name="printTableRow" />
</xsl:for-each>
</xsl:template>
<xsl:template match="AmazonBestSelling" mode="numericSort">
<xsl:for-each select="Book">
<xsl:sort select="*[name() = $SortBy]" data-type="number"/>
<xsl:call-template name="printTableRow" />
</xsl:for-each>
</xsl:template>
<xsl:template match="AmazonBestSelling" mode="dateSort">
<xsl:for-each select="Book">
<xsl:sort select="substring(*[name() = $SortBy], 7,4)" order="descending"/>
<xsl:sort select="substring(*[name() = $SortBy], 1,2)" order="descending"/>
<xsl:sort select="substring(*[name() = $SortBy], 3,2)" order="descending"/>
<xsl:call-template name="printTableRow" />
</xsl:for-each>
</xsl:template>
<xsl:template name="printTableRow">
<tr class="bookCell">
<td><xsl:value-of select="Rank"/></td>
<td><a href="/bookredirect.asp?isbn={@ISBN}"><xsl:value-of select="Title"/></a></td>
<td><xsl:value-of select="Author"/></td>
<td><xsl:value-of select="PublishDate"/></td>
<td>$<xsl:value-of select="Price"/></td>
</tr>
</xsl:template>
</xsl:stylesheet>
When the above stylesheet is applied on the books XML file, the generated HTML output is
by default is sorted on the Rank field. The column headings point to an ASP page, which can be used
to apply the transformation and pass the sort-field parameter to the stylesheet. The output looks similar
to the following screenshot:
Source Files:
| Source XML File |
XSLT Stylesheet |
Sample ASP page source code |
| BestBooks.xml |
BestBooks.xsl |
<%
Option Explicit
Dim objXMLDoc
Dim objXSLTDoc
Dim objTemplate
Dim objXSLTProc
Dim strParam1Val
Set objXMLDoc = Server.CreateObject("MSXML2.DOMDocument.4.0")
objXMLDoc.async=false
objXMLDoc.load Server.MapPath("BestBooks.xml")
Set objXSLTDoc = Server.CreateObject("MSXML2.FreeThreadedDOMDocument.4.0")
objXSLTDoc.load Server.MapPath("BestBooks.xsl")
Set objTemplate = Server.CreateObject("MSXML2.XSLTemplate.4.0")
Set objTemplate.stylesheet = objXSLTDoc
Set objXSLTProc = objTemplate.createProcessor()
objXSLTProc.input = objXMLDoc
strParam1Val = Request("s")
If Len(strParam1Val) > 0 Then
objXSLTProc.addParameter "SortBy", strParam1Val
End If
objXSLTProc.Transform
Response.Write objXSLTProc.output
%>
|
The stylesheet begins by declaring the output method type as HTML (xsl:output method="html").
The stylesheet accepts a parameter named SortBy, which has the default
value of Rank.
Next, you'll see some xsl:variable and xsl:if instructions used to
print (*) next to the column on which the data is sorted. The stylesheet
then generates the HTML as usual. The important part is the xsl:choose block, which depending
on the SortBy calls xsl:apply-templates with
the respective mode.
Note how the xsl:sort instruction dynamically sorts based on the input
stylesheet parameter (xsl:sort select="*[name() = $SortBy]").
For string sorting the data-type on xsl:sort is
set to text (which is the default); for numeric sorting it is
set to number, and for date sorting,
we have three xsl:sort instructions first to sort on year,
then on month, and finally on the day - the assumption here is that dates are always in the format mm/dd/yyyy.
The xsl:sort instruction(s) are written inside the
xsl:for-each select="Book" block. A named template,
printTableRow, is then called, which finally prints the data row by getting the
books details from the source XML file.
Click here to
view the sample ASP page which uses the above stylesheet; click on the column headings to sort the table data.
| XSLT elements illustrated in this example |
Functions illustrated in this example |
|
|
|
Summary
Go ahead and try out the examples presented this article and see how XSLT is used to transform the well-formed XML into various other text formats, and how the sorting is performed. The next part in this article series will illustrate some other very common XSLT tasks, such as looping, recursion, and generating nodes. Stay tuned!
Resources
|
|