position
Technology : XML
XSLT

eXtensible Stylesheet Language for Transformations

XSLT provides a mechanism that transforms an XML document into another.

For example a document matching a custom schema can be converted to html or to smil.

This technique is allows to convert a single data source into multiple outputs.

Take the following example:

We have an xml data source :

<Page xmlns="http://www.torrent-universe.com/schema/Page.xsd">
<Title>
<Paragraph Language="English">XML Schema</Paragraph>
<Paragraph Language="French">Schémas XML</Paragraph>
</Title>
<Subtitle>
<Paragraph Language="English">Custom XML Namespaces</Paragraph>
<Paragraph Language="French">Création de languages XML Personnalisés</Paragraph>
</Subtitle>
<Abstract>
<Paragraph Language="English">XML Schemas provide a means for defining the structure, content and semantics of XML documents.</Paragraph>
<Paragraph Language="French">Les schémas XML permettent de définir des structures de documents XML.</Paragraph>
</Abstract>
<Body>
<Paragraph Language="English">This document complies with the “http://www.torrent-universe.com/schema/Page.xsd” namespace.</Paragraph>
<Paragraph Language="French">Ce document repecte la définition “http://www.torrent-universe.com/schema/Page.xsd”.</Paragraph>
</Body>
</Page>

and an xsl stylesheet :

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.w3.org/TR/xhtml1" xmlns:p="http://www.torrent-universe.com/schema/Page.xsd">
<xsl:output method="xml" encoding="utf-8" version="1.0" omit-xml-declaration="no" media-type="text/xml" indent="yes"/>
<xsl:variable name="Language">English</xsl:variable>
<xsl:template match="/">
<html xmlns="http://www.w3.org/TR/xhtml1">
<xsl:apply-templates select="child::p:Page" mode="htmlDocument"/>
</html>
</xsl:template>
<xsl:template match="p:Page" mode="htmlDocument">
<head>
<xsl:apply-templates select="child::p:Title" mode="htmlTitle"/>
<link rel="stylesheet" type="text.css" href="page.css"/>
</head>
<body>
<div class="PageContent">
<xsl:apply-templates select="child::p:Title" mode="htmlBody"/>
<xsl:apply-templates select="child::p:Subtitle" mode="htmlBody"/>
<xsl:apply-templates select="child::p:Abstract" mode="htmlBody"/>
<xsl:apply-templates select="child::p:Body" mode="htmlBody"/>
</div>
</body>
</xsl:template>
<xsl:template match="p:Title" mode="htmlTitle">
<title>
<xsl:value-of select="child::p:Paragraph[@Language = $Language]"/>
</title>
</xsl:template>
<xsl:template match="p:Title|p:Subtitle|p:Abstract|p:Body" mode="htmlBody">
<div>
<xsl:attribute name="class">
<xsl:value-of select="local-name()"/>
</xsl:attribute>
<xsl:apply-templates select="child::p:Paragraph[@Language = $Language]" mode="htmlBody"/>
</div>
</xsl:template>
<xsl:template match="p:Paragraph" mode="htmlBody">
<p>
<xsl:apply-templates select="child::text()" mode="htmlBody"/>
</p>
</xsl:template>
<xsl:template match="text()" mode="htmlBody">
<xsl:value-of select="."/>
</xsl:template>
</xsl:stylesheet>

compiling the xml data through the xsl stylehseet will give :

<html>
<head>
<title>XML Schema</title>
<link rel="stylesheet" type="text.css" href="page.css"/>
</head>
<body>
<div class="PageContent">
<div class="Title">
<p>XML Schema</p>
</div>
<div class="Subtitle">
<p>Custom XML Namespaces</p>
</div>
<div class="Abstract">
<p>XML Schemas provide a means for defining the structure, content and semantics of XML documents.</p>
</div>
<div class="Body">
<p>This document complies with the “http://www.torrent-universe.com/schema/Page.xsd” namespace.</p>
</div>
</div>
</body>
</html>

The html markup above will be interpreted as follows by a web browser:

XML Schema

XML Schema

Custom XML Namespaces

XML Schemas provide a means for defining the structure, content and semantics of XML documents.

This document complies with the “http://www.torrent-universe.com/schema/Page.xsd” namespace.

All right, that doesn´t relate to any business situation. Let´s consider another example.

Imagine a situation where we have an invoice and we want to sum up the costs of apples, oranges and bananas.

The business data would be issued by a business tier or a data tier and would come as follows:

<order>
<invoice>
<item>
<name>apples</name>
<quantity>2</quantity>
</item>
<item>
<name>oranges</name>
<quantity>2</quantity>
</item>
<item>
<name>bananas</name>
<quantity>3</quantity>
</item>
</invoice>
<pricelist>
<item>
<name>apples</name>
<unitprice>3.00</unitprice>
</item>
<item>
<name>oranges</name>
<unitprice>4.00</unitprice>
</item>
<item>
<name>bananas</name>
<unitprice>5.00</unitprice>
</item>
</pricelist>
</order>

Note that the price list section is distinct from the invoice section, as the two may come from different data sources.

To convert that data into an html table, we would need an xslt template:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.w3.org/1999/xhtml">
<xsl:preserve-space elements="span"/>
<xsl:output method="xml" version="1.0" encoding="utf-8" omit-xml-declaration="no" media-type="text/html" indent="yes"/>
<xsl:template match="/">
<html>
<head>
<title>invoice</title>
<style type="text/css">td.number{text-align:right}</style>
</head>
<body>
<xsl:apply-templates select="//invoice" mode="table"/>
</body>
</html>
</xsl:template>
<xsl:template match="invoice" mode="table">
<table class="invoice">
<tr>
<th>Product</th>
<th>Quantity</th>
<th>Unit Price</th>
<th>Price</th>
</tr>
<xsl:apply-templates select="item" mode="table"/>
<tr class="total">
<td colspan="3">total</td>
<td class="number">
<xsl:apply-templates select="self::invoice" mode="total"/>
</td>
</tr>
</table>
</xsl:template>
<xsl:template match="item" mode="table">
<xsl:variable name="UnitPrice">
<xsl:apply-templates select="self::item" mode="unitprice"/>
</xsl:variable>
<xsl:variable name="Price">
<xsl:apply-templates select="self::item" mode="price"/>
</xsl:variable>
<tr>
<xsl:apply-templates select="name" mode="table"/>
<td class="number">
<xsl:value-of select="format-number(number(quantity), '#,###')"/>
</td>
<td class="number">
<xsl:value-of select="format-number(number($UnitPrice), '#,###.00')"/>
</td>
<td class="number">
<xsl:value-of select="format-number(number($Price), '#,###.00')"/>
</td>
</tr>
</xsl:template>
<xsl:template match="item/name" mode="table">
<td>
<xsl:value-of select="."/>
</td>
</xsl:template>
<xsl:template match="invoice" mode="total">
<script language="JavaScript">
<xsl:text>var numTotal = new Number(0);</xsl:text>
<xsl:for-each select="item"> numTotal +=
<xsl:apply-templates select="self::item" mode="price"/>;
</xsl:for-each>
<xsl:text>document.write(parseInt(numTotal) + "." + parseInt((numTotal - parseInt(numTotal)) * 10) + "" + parseInt((numTotal - parseInt(numTotal)) * 100 - parseInt((numTotal - parseInt(numTotal)) * 10)));</xsl:text>
</script>
</xsl:template>
<xsl:template match="item" mode="unitprice">
<xsl:variable name="ItemName">
<xsl:value-of select="name"/>
</xsl:variable>
<xsl:value-of select="number(//pricelist/item[name = $ItemName]/unitprice)"/>
</xsl:template>
<xsl:template match="item" mode="price">
<xsl:variable name="ItemQuantity">
<xsl:value-of select="number(quantity)"/>
</xsl:variable>
<xsl:variable name="ItemUnitPrice">
<xsl:apply-templates select="." mode="unitprice"/>
</xsl:variable>
<xsl:value-of select="number($ItemQuantity) * number($ItemUnitPrice)"/>
</xsl:template>
</xsl:stylesheet>

The resulting html would be:

<html>
<head>
<title>invoice</title>
<style type="text/css">td.number{text-align:right}</style>
</head>
<body>
<table class="invoice">
<tr>
<th>Product</th>
<th>Quantity</th>
<th>Unit Price</th>
<th>Price</th>
</tr>
<tr>
<td>apples</td>
<td class="number">2</td>
<td class="number">3.00</td>
<td class="number">6.00</td>
</tr>
<tr>
<td>oranges</td>
<td class="number">2</td>
<td class="number">4.00</td>
<td class="number">8.00</td>
</tr>
<tr>
<td>bananas</td>
<td class="number">3</td>
<td class="number">5.00</td>
<td class="number">15.00</td>
</tr>
<tr class="total">
<td colspan="3">total</td>
<td class="number">
<script language="JavaScript">var numTotal = new Number(0); numTotal += 6; numTotal += 8; numTotal += 15; document.write(parseInt(numTotal) + "." + parseInt((numTotal - parseInt(numTotal)) * 10) + "" + parseInt((numTotal - parseInt(numTotal)) * 100 - parseInt((numTotal - parseInt(numTotal)) * 10)));</script>
</td>
</tr>
</table>
</body>
</html>

When interpreted by a web browser this html markup gives the following table:

invoice
ProductQuantityUnit PricePrice
apples23.006.00
oranges24.008.00
bananas35.0015.00
total

Conclusion: XSLT transformations can get pretty complex as they implement many programming features such as control flow, loose-type variable, built-in functions, and a query syntax called XPath.

However, anyone acheiving similar results with string handling like “response.write("<td>"+strPrice+"</td>")” or with dom manipulations like “elTableRow.appendChild(elTableCell)” will find XSLT considerably simpler and easier to debug.

Additional Information on XSLT :

w3 Recommendation
XSLT by Microsoft