perfectxml.com
 Basic Search  Advanced Search   
Topics Resources Free Library Software XML News About Us
homeinfo bankarticles ╗ Common XSLT Tasks Ц Part I (Transformation and Sorting) Sat, Feb 23, 2008
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:
Transforming XML - to - HTML


Source Files:
Source XML File XSLT Stylesheet Source XML file with <?xml-stylesheet...?> Processing Instruction
Invoice.xml Invoice.xsl InvoiceWithXSLPI.xml


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:
Source XML File XSLT Stylesheet
Config.xml ConfigINI.xsl


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:
Source XML File XSLT Stylesheet Output XML File
lowercase.xml ToUpper.xsl uppercase.xml


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:
Transforming XML - to - SVG


Source Files:
Source XML File XSLT Stylesheet Ouput SVG File
Sales2002.xml BarChart.xsl Output.svg


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:
XSLT and Sorting


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

  Contact Us | E-mail Us | Site Guide | About PerfectXML | Advertise ©2004 perfectxml.com. All rights reserved. | Privacy