XSL Transformations (XSLT)

For programmers laboring in the trenches with XML, XSLT is one of the brightest stars in the XML universe. XSLT stands for Extensible Stylesheet Language Transformations. It is a language for converting XML documents from one format to another. Although it can be applied in a variety of ways, XSLT enjoys two primary uses:

The first application鈥攖urning XML into HTML鈥攊s useful for building Web pages and other browser-based documents in XML. XML defines the content and structure of data, but it doesn鈥檛 define the data鈥檚 appearance. Using XSLT to generate HTML from XML is a fine way to separate content from appearance and to build generic documents that can be displayed however you want them displayed. You can also use cascading style sheets (CSS) to layer appearance over XML content, but XSLT is more versatile than CSS and provides substantially more control over the output.

XSLT is also used to convert XML document formats. Suppose company A expects XML invoices submitted by company B to conform to a particular format (that is, fit a particular schema), but company B already has an XML invoice format and doesn鈥檛 want to change it to satisfy the whims of company A. Rather than lose company B鈥檚 business, company A can use XSLT to convert invoices submitted by company B to company A鈥檚 format. That way both companies are happy, and neither has to go to extraordinary lengths to work with the other. XML-to-XML XSLT conversions are the nucleus of middleware applications such as Microsoft BizTalk Server that automate business processes by orchestrating the flow of information.

Figure 13-13 illustrates the mechanics of XSLT. You feed a source document (the XML document to be transformed) and an XSL style sheet that describes how the document is to be transformed to an XSLT processor. The XSLT processor, in turn, generates the output document using the rules in the style sheet.

Figure 13-13
XSL Transformations.

MSXML is an XSLT processor. So is the XslTransform class located in the FCL鈥檚 System.Xml.Xsl namespace. XslTransform is one of the coolest classes in the FCL. It鈥檚 exceedingly simple to use, and it鈥檚 an essential tool to have at your disposal when the need arises to programmatically convert XML documents from one format to another. The following sections describe how to put XslTransform to work.

Converting XML to HTML on the Client

If you鈥檝e never worked with XSLT before, a great way to get acquainted with it is to build a simple XML document and transform it to HTML using Internet Explorer (which, under the hood, relies on MSXML to perform XSL transformations). Here鈥檚 how:

  1. Copy Figure 13-16鈥檚 Guitars.xml and Guitars.xsl to the directory of your choice.

  2. Temporarily delete (or comment out) the following statement in Guitars.xml:

    <?xml-stylesheet聽type="text/xsl" href="Guitars.xsl"?>
  3. Open Guitars.xml in Internet Explorer. The file is displayed as XML (Figure 13-14).

  4. Restore (or uncomment) the statement you deleted (or commented out) in step 2.

  5. Open Guitars.xml again in Internet Explorer. This time, the file is displayed as HTML (Figure 13-15).

    Figure 13-14
    Guitars.xml displayed without XSLT.
    Figure 13-15
    Guitars.xml displayed with XSLT.
    Guitars.xml
    <?xml聽version="1.0"?>
    <?xml-stylesheet聽type="text/xsl" href="Guitars.xsl"?>
    <Guitars>
    聽聽<Guitar>
    聽聽聽聽<Make>Gibson</Make>
    聽聽聽聽<Model>SG</Model>
    聽聽聽聽<Year>1977</Year>
    聽聽聽聽<Color>Tobacco聽Sunburst</Color>
    聽聽聽聽<Neck>Rosewood</Neck>
    聽聽</Guitar>
    聽聽<Guitar>
    聽聽聽聽<Make>Fender</Make>
    聽聽聽聽<Model>Stratocaster</Model>
    聽聽聽聽<Year>1990</Year>
    聽聽聽聽<Color>Black</Color>
    聽聽聽聽<Neck>Maple</Neck>
    聽聽</Guitar>
    </Guitars>
    Figure 13-16
    XML file and an XSL style sheet for transforming it into HTML.
    Guitars.xsl
    <?xml聽version="1.0"?>
    <xsl:stylesheet聽xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    聽聽version="1.0">
    聽聽<xsl:template聽match="/">
    聽聽聽聽<html>
    聽聽聽聽聽聽<body>
    聽聽聽聽聽聽聽聽<h1>My聽Guitars</h1>
    聽聽聽聽聽聽聽聽<hr聽/>
    聽聽聽聽聽聽聽聽<table聽width="100%" border="1">
    聽聽聽聽聽聽聽聽聽聽<tr聽bgcolor="gainsboro">
    聽聽聽聽聽聽聽聽聽聽聽聽<td><b>Make</b></td>
    聽聽聽聽聽聽聽聽聽聽聽聽<td><b>Model</b></td>
    聽聽聽聽聽聽聽聽聽聽聽聽<td><b>Year</b></td>
    聽聽聽聽聽聽聽聽聽聽聽聽<td><b>Color</b></td>
    聽聽聽聽聽聽聽聽聽聽聽聽<td><b>Neck</b></td>
    聽聽聽聽聽聽聽聽聽聽</tr>
    聽聽聽聽聽聽聽聽聽聽<xsl:for-each聽select="Guitars/Guitar">
    聽聽聽聽聽聽聽聽聽聽聽聽<tr>
    聽聽聽聽聽聽聽聽聽聽聽聽聽聽<td><xsl:value-of聽select="Make" /></td>
    聽聽聽聽聽聽聽聽聽聽聽聽聽聽<td><xsl:value-of聽select="Model" /></td>
    聽聽聽聽聽聽聽聽聽聽聽聽聽聽<td><xsl:value-of聽select="Year" /></td>
    聽聽聽聽聽聽聽聽聽聽聽聽聽聽<td><xsl:value-of聽select="Color" /></td>
    聽聽聽聽聽聽聽聽聽聽聽聽聽聽<td><xsl:value-of聽select="Neck" /></td>
    聽聽聽聽聽聽聽聽聽聽聽聽</tr>
    聽聽聽聽聽聽聽聽聽聽</xsl:for-each>
    聽聽聽聽聽聽聽聽</table>
    聽聽聽聽聽聽</body>
    聽聽聽聽</html>
    聽聽</xsl:template>
    </xsl:stylesheet>

What happened? The statement

<?xml-stylesheet聽type="text/xsl" href="Guitars.xsl"?>

is a processing instruction informing Internet Explorer that Guitars.xsl is a style sheet containing instructions for converting the content found in Guitars.xml to another format. IE downloaded the style sheet and ran it against Guitars.xml, producing HTML.

Most XSL transformations are template-driven. In Guitars.xsl, the statement

<xsl:template聽match="/">

marks the beginning of a template that applies to the entire document. 鈥?鈥?is an XPath expression that signifies the root of the document. The first several statements inside the template output the beginnings of an HTML document that includes an HTML table. The for-each element iterates over all the Guitar elements that are subelements of Guitars (note the XPath expression 鈥淕uitar/Guitars鈥?defining the selection). Each iteration adds another row to the table, and the value-of elements initialize the table鈥檚 cells with the values of the corresponding XML elements.

Converting XML to HTML on the Server

That鈥檚 one way to convert XML to HTML. One drawback to this approach is that the XML document must contain a processing directive pointing to the style sheet. Another drawback is that the transformation is performed on the client. It doesn鈥檛 work in IE 4 and probably won鈥檛 work in most third-party browsers because XSLT wasn鈥檛 standardized until recently. Unless you can control the browsers that your clients use, it behooves you to perform the transformation on the server where you can be sure an up-to-date XSLT processor is available.

How do you perform XSL transformations on the server? With XslTransform, of course. The Web page in Figure 13-18鈥擰uotes.aspx鈥攄emonstrates the mechanics. It contains no HTML; just a server-side script that generates HTML from an XML input document named Quotes.xml and a style sheet named Quotes.xsl. XslTransform.Transform performs the transformation. Its first parameter is an XPathDocument object wrapping the source document. The third parameter specifies the destination for the output, which in this example is the HTTP response. The second parameter, which is not used here, is an XsltArgumentList containing input arguments. Before calling XslTransform.Transform, Quotes.aspx calls another XslTransform method named Load to load the style sheet that governs the conversion. The resulting Web page is shown in Figure 13-17.

Figure 13-17
Output from Quotes.aspx.
Quotes.aspx
<%@聽Page聽Language="C#" %>
<%@聽Import聽Namespace="System.Xml.XPath" %>
<%@聽Import聽Namespace="System.Xml.Xsl" %>

<%
聽聽XPathDocument聽doc聽=
聽聽聽聽聽聽new聽XPathDocument聽(Server.MapPath聽("Quotes.xml"));
聽聽XslTransform聽xsl聽=聽new聽XslTransform聽();
聽聽xsl.Load聽(Server.MapPath聽("Quotes.xsl"));
聽聽xsl.Transform聽(doc,聽null,聽Response.OutputStream);
%>
Figure 13-18
Web page that converts XML to HTML on the server.
Quotes.xml
<?xml聽version="1.0"?>
<Quotes>
聽聽<Quote>
聽聽聽聽<Text>Give聽me聽chastity聽and聽continence,聽but聽not聽yet.</Text>
聽聽聽聽<Author>Saint聽Augustine</Author>
聽聽</Quote>
聽聽<Quote>
聽聽聽聽<Text>The聽use聽of聽COBOL聽cripples聽the聽mind;聽its聽teaching聽should聽
聽聽聽聽聽聽therefore聽be聽regarded聽as聽a聽criminal聽offense.</Text>
聽聽聽聽<Author>Edsger聽Dijkstra</Author>
聽聽</Quote>
聽聽<Quote>
聽聽聽聽<Text>C聽makes聽it聽easy聽to聽shoot聽yourself聽in聽the聽foot;聽C++聽makes聽it聽
聽聽聽聽聽聽harder,聽but聽when聽you聽do,聽it聽blows聽away聽your聽whole聽leg.</Text>
聽聽聽聽<Author>Bjarne聽Stroustrup</Author>
聽聽</Quote>
聽聽<Quote>
聽聽聽聽<Text>A聽programmer聽is聽a聽device聽for聽turning聽coffee聽into聽code.</Text>
聽聽聽聽<Author>Jeff聽Prosise聽(with聽an聽assist聽from聽Paul聽Erdos)</Author>
聽聽</Quote>
聽聽<Quote>
聽聽聽聽<Text>I聽have聽not聽failed.聽I&apos;ve聽just聽found聽10,000聽ways聽that聽
聽聽聽聽聽聽won&apos;t聽work.</Text>
聽聽聽聽<Author>Thomas聽Edison</Author>
聽聽</Quote>
聽聽<Quote>
聽聽聽聽<Text>Blessed聽is聽the聽man聽who,聽having聽nothing聽to聽say,聽abstains聽from聽
聽聽聽聽聽聽giving聽wordy聽evidence聽of聽the聽fact.</Text>
聽聽聽聽<Author>George聽Eliot</Author>
聽聽</Quote>
聽聽<Quote>
聽聽聽聽<Text>I聽think聽there聽is聽a聽world聽market聽for聽maybe聽five聽
聽聽聽聽聽聽computers.</Text>
聽聽聽聽<Author>Thomas聽Watson</Author>
聽聽</Quote>
聽聽<Quote>
聽聽聽聽<Text>Computers聽in聽the聽future聽may聽weigh聽no聽more聽than聽1.5聽
聽聽聽聽聽聽tons.</Text>
聽聽聽聽<Author>Popular聽Mechanics</Author>
聽聽</Quote>
聽聽<Quote>
聽聽聽聽<Text>I聽have聽traveled聽the聽length聽and聽breadth聽of聽this聽country聽and聽
聽聽聽聽聽聽talked聽with聽the聽best聽people,聽and聽I聽can聽assure聽you聽that聽data聽
聽聽聽聽聽聽processing聽is聽a聽fad聽that聽won&apos;t聽last聽out聽the聽year.</Text>
聽聽聽聽<Author>Prentice-Hall聽business聽books聽editor</Author>
聽聽</Quote>
聽聽<Quote>
聽聽聽聽<Text>640K聽ought聽to聽be聽enough聽for聽anybody.</Text>
聽聽聽聽<Author>Bill聽Gates</Author>
聽聽</Quote>
</Quotes>
Quotes.xsl
<?xml聽version="1.0"?>
<xsl:stylesheet聽xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
聽聽version="1.0">
聽聽<xsl:template聽match="/">
聽聽聽聽<html>
聽聽聽聽聽聽<body>
聽聽聽聽聽聽聽聽<h1聽style="background-color:聽teal;聽color:聽white;
聽聽聽聽聽聽聽聽聽聽聽font-size:聽24pt;聽text-align:聽center;聽letter-spacing:聽1.0em">
聽聽聽聽聽聽聽聽聽聽Famous聽Quotes
聽聽聽聽聽聽聽聽</h1>
聽聽聽聽聽聽聽聽<table聽border="1">
聽聽聽聽聽聽聽聽聽聽<tr聽style="font-size:聽12pt;聽font-family:聽verdana;
聽聽聽聽聽聽聽聽聽聽聽聽font-weight:聽bold">
聽聽聽聽聽聽聽聽聽聽聽聽<td聽style="text-align:聽center">Quote</td>
聽聽聽聽聽聽聽聽聽聽聽聽<td聽style="text-align:聽center">Author</td>
聽聽聽聽聽聽聽聽聽聽</tr>
聽聽聽聽聽聽聽聽聽聽<xsl:for-each聽select="Quotes/Quote">
聽聽聽聽聽聽聽聽聽聽聽聽<xsl:sort聽select="Author" />
聽聽聽聽聽聽聽聽聽聽聽聽<tr聽style="font-size:聽10pt;聽font-family:聽verdana">
聽聽聽聽聽聽聽聽聽聽聽聽聽聽<td><xsl:value-of聽select="Text"/></td>
聽聽聽聽聽聽聽聽聽聽聽聽聽聽<td><i><xsl:value-of聽select="Author"/></i></td>
聽聽聽聽聽聽聽聽聽聽聽聽</tr>
聽聽聽聽聽聽聽聽聽聽</xsl:for-each>
聽聽聽聽聽聽聽聽</table>
聽聽聽聽聽聽</body>
聽聽聽聽</html>
聽聽</xsl:template>
</xsl:stylesheet>

As you might guess, there鈥檚 much more to XSLT than these examples demonstrate. In addition to supporting for-each and value-of elements, for example, XSLT supports if and choose elements for conditional branching, variable elements for declaring variables, sort elements for sorting (look closely and you鈥檒l see sort used in Quotes.xsl), and a whole lot more. For a complete list of elements and a summary of XSLT鈥檚 syntax, refer to the XSLT specification at http://www.w3.org/TR/xslt.

Converting XML Document Formats

To put an exclamation point at the end of this chapter, Figure 13-19 contains the source code for a simple application that transforms an XML document using the specified XSL style sheet and writes the results to the host console window. It鈥檚 a handy tool for debugging style sheets by previewing their output. The following command lists the HTML that鈥檚 generated when Quotes.xml is transformed with Quotes.xsl:

transform聽quotes.xml聽quotes.xsl

As you can see, the XslTransform class makes such a utility exceedingly easy to write鈥攋ust one more piece of evidence of how valuable and capable a tool the .NET Framework class library is for reading, writing, and manipulating XML documents.

Transform.cs
using聽System;
using聽System.Xml.XPath;
using聽System.Xml.Xsl;

class聽MyApp
{
聽聽聽聽static聽void聽Main聽(string[]聽args)
聽聽聽聽{
聽聽聽聽聽聽聽聽if聽(args.Length聽<聽2)聽{
聽聽聽聽聽聽聽聽聽聽聽聽Console.WriteLine聽("Syntax:聽TRANSFORM聽xmldoc聽xsldoc");
聽聽聽聽聽聽聽聽聽聽聽聽return;
聽聽聽聽聽聽聽聽}

聽聽聽聽聽聽聽聽try聽{
聽聽聽聽聽聽聽聽聽聽聽聽XPathDocument聽doc聽=聽new聽XPathDocument聽(args[0]);
聽聽聽聽聽聽聽聽聽聽聽聽XslTransform聽xsl聽=聽new聽XslTransform聽();
聽聽聽聽聽聽聽聽聽聽聽聽xsl.Load聽(args[1]);
聽聽聽聽聽聽聽聽聽聽聽聽xsl.Transform聽(doc,聽null,聽Console.Out);
聽聽聽聽聽聽聽聽}
聽聽聽聽聽聽聽聽catch聽(Exception聽ex)聽{
聽聽聽聽聽聽聽聽聽聽聽聽Console.WriteLine聽(ex.Message);
聽聽聽聽聽聽聽聽}
聽聽聽聽}
}
Figure 13-19
Utility for previewing XSL transformations.