perfectxml.com
 Basic Search  Advanced Search   
Topics Resources Free Library Software XML News About Us

Transformations

For the development community, one of the most useful features when working with XML is the ability to transform XML content to another format more appropriate for a given situation. To harness this power in ASP.NET, we first of all need to understand when and why we should use transformations in a web application. Once we've made that decision, we must understand the syntax necessary to create our transformation and how to execute the transformation in the managed environment of .NET.

 

By the end of this chapter, we will have covered the following subjects:

 

§          What is XSLT?

§          Structure of an XSLT document

§          Applying XSLT Style Sheets to XML documents

§          Controlling document output

§          Using transformations for the presentation layer

§          Using transformations for B2B

 

We will learn how to harness the XPath skills acquired from the previous chapter, and examine how to use transformations effectively in ASP.NET.

 

What is XSLT?

Extensible Stylesheet Language Transformations (XSLT) is a declarative programming language, with its origins in the early Extensible Stylesheet Language (XSL). XSLT v1.0 was endorsed by the Director of W3C as a Recommendation in November 1999, and more information can be found at http://www.w3.org/TR/xslt. We will be using version 1.0 in this chapter, as supported by the .NET Framework, although there are other versions in working draft at the time of writing.

 

Transforming XML Documents

XSLT is the language which instructs an XSLT processor how to convert information in an XML source document to a format of our preference – be it an XML document (including WML for example), an HTML document, or just plain text. Note that different XSLT engines will adhere to the standard to differing levels, but in this chapter, we will naturally concentrate on the behavior of .NET.

From XML to XML

There are many situations where there is a need to transform an XML document to one in a completely different XML dialect. For example, consider the following document extract:

 

<Item>

   <ID>ITM-1001</ID>

   <LineOfProduct>1</LineOfProduct>

   <ListPrice>123.45</ListPrice>

   <QTY>1</QTY>

   <Description>Gadget XYZ</Description>

 </Item>

 

We may prefer to have this information in a different form, perhaps for a component we have already developed which handles data in this form:

 

<item id="ITM-1001" productLine="1">

   <quantity>1</quantity>

   <list-price>123.45</list-price>

   <description>Gadget XYZ</description>

 </item>

 

Notice how the first extract is element-centric; it is devoid of any attributes. Also the first uses a different nomenclature for node names, like <QTY> vs. <quantity>, and we can't forget that XML documents are case-sensitive – thus <Description> is different from <description>.

 

So how can we transform one to the other? We could load the first XML document into an XmlDocument object, traverse each node and programmatically generate a second XmlDocument object. This would work, but what if we needed to make changes to the transformation? It could be quite a challenge to locate and change the code to create the new transformation. Also, the programmatic route requires recompiling the code after any such changes.

 

The preferred method would be to use XSLT style sheets. After all, the language is designed specifically for this purpose. Secondly, it is fairly easy to locate the template rules that perform certain aspects of a transformation (discussed later) and add, update, or delete parts to create new transformations. Finally, it is not necessary to recompile and redeploy the code which references an XSLT style sheet that has been changed.

 

From XML to HTML

In ASP.NET applications, it is quite common to encounter a need to present data provided as XML to the user in HTML. A typical example of this would be a symmetrical XML document that quite easily lends itself to a table format. For instance, we may be interested in taking the following XML structure:

 

<items>

   <item id="ITM-1001" productLine="1">

     <quantity>1</quantity>

      <list-price>123.45</list-price>

      <description>Gadget XYZ</description>

   </item>

   <item id="ITM-1002" productLine="1">

     <quantity>3</quantity>

      <list-price>4.00</list-price>

      <description>XYZ Accessory</description>

   </item>   <item id="ITM-1003" productLine="2">

     <quantity>1</quantity>

      <list-price>15.00</list-price>

      <description>Gizmo Part</description>

   </item>

   <item id="ITM-1004" productLine="3">

     <quantity>1</quantity>

      <list-price>44.00</list-price>

      <description>Widget X</description>

   </item>

 </items>

 

and presenting it to the user like this:

 

 

The markup to create the table above would be enclosed within an HTML <table> tag. By using XSLT, we can take any XML structure and convert it to HTML. We will see how to do this a little later.

 

From XML to Plain Text

From time to time, we may need to generate plain text. Typically, this is done to support legacy applications that consume text documents with either fixed length or comma delimited columns. Thus the <items> element and all of its children from the previous XML file could be transformed to a comma delimited text file like this:

 

ITM-1001,  1,  1,  123.45,  Gadget ZYZ

ITM-1002,  1,  3,  4.00,    XYZ Accessory

ITM-1003,  2,  1,  15.00,  Gizmo Part

ITM-1004,  3,  1,  44.00,  Widget X

Structure of an XSLT Style Sheet

Now that we have a glimpse of what XSLT style sheets can do, the next step is to examine the actual structure of a style sheet. Of course the subject of XSLT style sheets is enough to fill an entire book, so our objective in this chapter will be to provide what you need to know in order to use its features in ASP.NET applications.

 

 

XSLT is a Declarative, Rule-Based Language

Unlike procedural languages, which are executed in sequential order, XSLT is a declarative, rule-based language where the XSLT processor determines the execution sequence. Simply stated, we describe the rules (which can appear in any order), and the processor parses these rules and applies them to produce a corresponding output document.

 

XSLT is XML

An XSLT style sheet is an XML document. All the rules that specify the behavior of a style sheet are contained within XML elements belonging to the XSLT namespace.

 

The <stylesheet> Document Element

The document or root element of the style sheet is <stylesheet>. Like the XML declaration node, it contains a version attribute, which we will set to "1.0". Typically, the prefix xsl is used to reference the XSLT namespace http://www.w3.org/1999/XSL/Transform. Therefore, the declaration usually looks like this:

 

<?xml version="1.0" encoding="UTF-8"?>

 <xsl:stylesheet version="1.0"

      xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >

 </xsl:stylesheet>

 

However, in keeping with standard XML rules, the xsl prefix is merely the suggested prefix for the required namespace declaration. In fact, when we create an XSLT file in Visual Studio .NET, it declares the XSLT namespace as the default.

 

Creating a Style Sheet in VS.NET

Open Visual Studio .NET and select File | New | File, or just press Ctrl-N. The following dialog box appears:

 

 

With the General folder in the Categories pane selected, select the XSLT File icon in the Templates pane. Click the Open button, and it will create an empty solution with a style sheet containing just the opening declaration as shown:

 

 

As mentioned earlier, Visual Studio .NET declares http://www.w3.org/1999/XSL/Tranform as the default namespace. Although this is the default behavior for Visual Studio .NET and won't normally cause any problems, we will use the xsl prefix in our examples this chapter. Alter the code like so:

 

<?xml version="1.0" encoding="UTF-8"?>

 <xsl:stylesheet version="1.0"

                 xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >

 </xsl:stylesheet>

 

Now save the file by selecting File | Save XSLTFile1.xslt as... and a file dialog box will appear. When prompted, enter the name transform-one.xslt. We will add content to this style sheet shortly.

 

Specifying the Output Type

As stated earlier, the output of a transformation can either be XML, HTML, plain text, or qualified names (the exotic sounding qname-but-not-ncname type). By default, the output is HTML. We can specify other output types by declaring an <output> element as a child of the <stylesheet> element with the appropriate method attribute set. For example, this is how we specify XML as the output type:

 

<xsl:output method="xml" />

 

Note that as a child of the <stylesheet> document element, this element is designated as a top-level element of the style sheet. Top level elements are immediate children of <stylesheet>.

 

Other attributes of this element control behavior such as whether to omit the XML declaration in the resulting document, or which version and encoding should be specified.

 

Defining Template Rules

Template rules are elements that describe how the XSLT processor should transform nodes that meet certain criteria in an XML document. We declare template rules by creating a top-level element named <template>, and specifying which node(s) it contains rules for using an appropriate XPath expression in its match attribute.

 

The Template Rule for the Root Node

It is good practice to always declare a template rule for the root node of the source document in your XSLT style sheets, and some XSLT processors will actually fail if it is not present. Below is the syntax for a template rule matching the root node:

 

<xsl:template match="/">

   <!-- Template rules go here -->

 </xsl:template>

 

Notice the XPath expression provided in the match attribute, which, as we learned in the previous chapter, identify the root node. The "/" character on its own is also the starting point for absolute location paths. Refer back to Chapter 4 if you need more information on XPath expressions and location paths.

 

Don't forget that <template> is a top-level element, which means it must always be an immediate child of <stylesheet>. We can define as many <template> elements as we need in a style sheet. What sets one template rule apart from another is the XPath expression in the match attribute, although we could have two templates with the same XPath expression, but differentiated by the mode attribute (covered in the Enhancing Template Rules section).

 

Specifying Template Rules for Other Nodes

The XPath expression in the match attribute of the <template> element identifies nodes in the source document that we wish to transform by applying that template. Let's illustrate this with the following XML document, which we've already seen in the previous chapter:

 

<?xml version="1.0" encoding="UTF-8" ?>

 <?custom-error code="0" message="OK" ?>

 <!--Shopping Cart Example-->

 <shopping-cart>

   <header>

     <customer id="P4LLC" billingId="001" shippingId="001" >

       <contact>Toshia Palermo</contact>

     </customer>

     <order-type>Regular</order-type>

   </header>

   <items>

     <item id="ITM-1001" productLine="1">

       <quantity>1</quantity>

       <list-price>123.45</list-price>

       <description>Gadget XYZ</description>

     </item>

     <item id="ITM-1002" productLine="1">

       <quantity>3</quantity>

       <list-price>4.00</list-price>

       <description>XYZ Accessory</description>

     </item>

     <item id="ITM-1003" productLine="2">

       <quantity>1</quantity>

       <list-price>15.00</list-price>

       <description>Gizmo Part</description>

     </item>

     <item id="ITM-1004" productLine="3">

       <quantity>1</quantity>

       <list-price>44.00</list-price>

       <description>Widget X</description>

     </item>

   </items>

 </shopping-cart>

 

This file, shopping-cart.xml, will be used in our examples throughout the chapter. The one difference with the file used in Chapter 4 is the absence of the namespace on the <shopping-cart> element, which has been removed for simplicity. The table below shows some example XPath expressions and their effect on the shopping-cart.xml document:

 

XPath Expression in match Attribute of <template> Element

Description

match="/shopping-cart"

This will match the <shopping-cart> element – the document element of the source XML. Remember that there can only ever be one document element in well- formed XML.

match="//item"

This matches any <item> elements in the source XML.

match="/*/items//@*"

This matches any attribute nodes that are descendants of the <items> element, which is itself a grandchild of the root node.

match="quantity"

This relative expression matches any <quantity> children of the context node.

 

Once the nodes are identified, the rules inside the <template> element describe the transformation to perform.

 

Accessing Values with <value-of>

Within the template, we can access each node matching the XPath expression as the context node. To obtain values from the source XML, we use the <value-of> element. It has an obligatory select attribute containing another XPath expression denoting the node whose value we want. The following example demonstrates this:

 

<xsl:template match="item">

   <div>

     Item=<xsl:value-of select="@id" />,    

      Quantity=<xsl:value-of select="quantity" />

   </div>

 </xsl:template>

 

 

The template rule in the above example matches <item> element children of the context node. Any such <item> element becomes in turn the context node for XPath expressions within the template. The first <xsl:value-of> element has a select attribute with an XPath expression locating the id attribute of the <item> element. The select attribute of the second <xsl:value-of> element provides a relative location path (with abbreviated syntax) to retrieve the value of the <quantity> child. These values are placed within an enclosing HTML <div>.

 

Earlier we indicated that the order in which template rules are defined is irrelevant, so how did the processor get to this template rule? The starting point is the template rule that matches the root node. From there, we can explicitly apply other templates to the elements we want to transform.

 

Applying Templates with <apply-templates>

The <apply-templates> element is used to transform nodes from within other templates. To apply transformations using the template in the previous example, we could use this element within the template for the root node, like so:

 

<xsl:template match="/">

   <html>

     <head>

       <title>Transformation Example</title>

     </head>

     <body>

       <xsl:apply-templates select="//item" />

     </body>

   </html>

 </xsl:template>

 

Here, we wrap the 'call' to the other template rule inside HTML markup, with the <apply- templates> element in the <body> tag. Our select attribute indicates that all of the <item> elements in the source XML document should be processed by any matching templates at this point. If we omit the select attribute, the parser would apply templates for all children of the context node.

 

The result of this call is similar to that produced by the SelectNodes method of the XmlNode object (or any of its descendants, such as XmlDocument). The XSLT processor builds a node-set in memory, and looks for a template rule that satisfies a match for each node. In our example, a node-list of four elements is generated, appearing in the set in the order in which they appear in the source document. The XSLT processor then processes each node one at a time by searching the style sheet for a matching template, such as this one:

 

<xsl:template match="item">

   <div>

     Item=<xsl:value-of select="@id" />,    

        Quantity=<xsl:value-of select="quantity" />

   </div>

 </xsl:template>

 

For the first <item> element in the document, this would produce the following output:

 

<div>Item=ITM-1001, Quantity=1</div>

 

The processor then moves on to the next node in the node-set, which in this case is also processed by the same template rule, and so on for each node in the node-set. The final output is this:

 

<div>Item=ITM-1001, Quantity=1</div>

<div>Item=ITM-1002, Quantity=3</div>

<div>Item=ITM-1003, Quantity=1</div>

<div>Item=ITM-1004, Quantity=1</div>

 

If we put all this together in the transform-one.xslt sheet, here is what it looks like in its entirety:

 

<?xml version="1.0" encoding="UTF-8" ?>

 

 <xsl:stylesheet version="1.0"

               xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >

 

   <xsl:template match="/">

     <html>

       <head>

         <title>Transformation Example</title>

       </head>

       <body>

         <xsl:apply-templates select="//item" />

       </body>

     </html>

   </xsl:template>

 

 <xsl:template match="item">

     <div>

     Name=<xsl:value-of select="@id" />,

     Quantity=<xsl:value-of select="quantity" />

     </div>

   </xsl:template>

 </xsl:stylesheet>

 

It is also worthy of note that we have ignored all the other nodes in the source XML document. We only need to define template rules for those nodes we need to process.

Applying an XSLT Style Sheet to an XML Document

Essentially, there are two ways to apply an XSLT style sheet to an XML document. We can either reference the style sheet in our XML document, or apply the style sheet programmatically. The first approach is considered static, while the latter is more dynamic.

 

Applying a Style Sheet Statically

To statically link an XSLT style sheet to an XML document, we add the <?xml-stylesheet?> processing directive to the start of the source XML. For instance, if the transform-one.xslt and the shopping-cart.xml files are in the same directory, we could add the following to the top of shopping-cart.xml:

 

 <?xml version="1.0" encoding="UTF-8" ?>

 <?xml-stylesheet type="text/xsl" href="transform-one.xslt" ?>

 <?custom-error code="0" message="OK" ?>

 <!--Shopping Cart Example-->

 <shopping-cart>

     . . .

     . . .

     . . .

 </shopping-cart>

 

The type attribute specifies that it is an XSLT style sheet we want to apply, as we could also specify a cascading style sheet by setting this attribute to "text/css". The href attribute supplies the location of the style sheet. If we open the XML document in a web browser, this is what we will see:

 

 

If we view the source for this page, we will see the source XML, not the XSLT output that produces the above display. In order to view the XSLT output in Internet Explorer, we can download a free plug-in from Microsoft's site at http://msdn.microsoft.com/msdn-files/027/000/543/iexmltls.exe.

 

Applying a Style Sheet Dynamically

To demonstrate how to apply a style sheet programmatically, let's create a new web application in Visual Studio .NET. Select File | New | Project to pull up the New Project dialog box. Create a new Visual C# ASP.NET web application project, and name it Transforms:

 

 

This will create the application and display the WebForm1.aspx page. Rename the WebForm1.aspx file in Solution Explorer to TransformXML.aspx.

 

Copy the shopping-cart.xml and transform-one.xslt files into the Transforms directory.

 

Now view the code-behind for the page, and change it as highlighted below:

 

using System;

using System.Collections;

using System.ComponentModel;

using System.Data;

using System.Drawing;

using System.Web;

using System.Web.SessionState;

using System.Web.UI;

using System.Web.UI.WebControls;

using System.Web.UI.HtmlControls;

using System.Xml.XPath;

using System.Xml.Xsl;

 

namespace Wrox.ASPNET.Transforms

{  

   /// <summary>  

   /// Summary description for TransformXML  

   /// </summary>  

 

   public class TransformXML : System.Web.UI.Page  

   {

     private void Page_Load(object sender, System.EventArgs e)

     {

       // Put user code to initialize the page here

     }

     [ Web Form Designer generated code ]

   }

 }

 

All we're doing is including the System.Xml.XPath and System.Xml.Xsl namespaces, and changing the namespace and class name.

 

Switch back to the TransformXML.aspx window, and view the HTML code noting how the Page processing directive has changed to reflect our changes:

 

<%@ Page language="c#" Codebehind="TransformXML.aspx.cs"

     AutoEventWireup="false" Inherits="Wrox.ASPNET.Transforms.TransformXML"%>

 

Remove all other markup on this page so that only the above line appears there.

 

Using XslTransform

Now find the Page_Load event handler, and add the following code:

 

private void Page_Load(object sender, System.EventArgs e)

{

   try

   {

     // Instantiate and Load the XPathDocument

     XPathDocument _SourceXml =

       new XPathDocument( Server.MapPath("shopping-cart.xml") );

 

     // Instantiate and load the XslTransform object

     XslTransform _Transform = new XslTransform();

     _Transform.Load( Server.MapPath("transform-one.xslt")) ;

 

     // Send result of transformation to Response.Output stream

     _Transform.Transform( _SourceXml, null, Response.Output );

   }

   catch (Exception handledException)

   {

     Response.Write(handledException.Message);

   }

 }

 

First, we load the source XML into an XPathDocument object. This object is similar to XmlDocument, but is highly optimized for XSLT processing. Next, we load the transform-one.xslt style sheet into an XslTransform object. XslTransform implements the .NET XSLT processing engine, which is invoked when we call its Transform method. In the example above, we're using the overloaded Transform method which takes the source XML, any parameters for the style sheet (none are required, so we provide null), and finally where to place the results of the transformation. Our results are directed to the HTTP response stream.

 

Hit F5 to compile and run the project. On the surface, the output is the same as if we applied the sheet statically. However, this time if we view the source, we see the XSLT processor output, not the source XML, because the transformation occurred on the server, not client-side in the browser. By applying the style sheet on the server, we can ensure it will be viewed correctly by a far greater range of browsers, as many do not support XSLT transformations natively.

 

Enhancing Template Rules

There are several situations that require special handling. In such cases, we can provide more context for our template match by providing a mode, or we can explicitly call a template by name, rather than by node match.

 

Before we investigate these topics, let's first make some changes to our Transforms web application. In the TransformXML.aspx.cs code file, make the following revisions to the Page_Load event handler:

 

// Put user code to initialize the page here

try

{

   string _XMLPath = Request.QueryString["xml"].ToString();

   string _XSLTPath = Request.QueryString["xslt"].ToString();

 

   // Instantiate and Load the XPathDocument

   XPathDocument _SourceXml = new XPathDocument( Server.MapPath(_XMLPath) );

 

   // Instantiate and load the XslTransorm object

   XslTransform _Transform = new XslTransform();

   _Transform.Load( Server.MapPath(_XSLTPath)) ;

 

   // Send result of transformation to Response.Output stream

   _Transform.Transform( _SourceXml, null, Response.Output );

 }

 catch (Exception handledException)

 {

   Response.Write(handledException.Message);

 }

 

These changes allow us to reuse the TransformXML.aspx page with any source document and style sheet using filenames supplied in the query string. For example, the following URL is equivalent to the previous example:

 

http://localhost/transforms/transformxml.aspx?xml=shopping-cart.xml&xslt=transform-one.xslt

 

Template Modes

Template modes are a great way to handle nodes based on a specific purpose. What if we want to perform two or more separate transformations on the same node-set? We can tailor the transformation by specifying a mode, in one of:

 

§          the calling <apply-templates> element

§          the receiving <template> element, containing the rules

 

Using our shopping-cart.xml file example, what if we wanted to first highlight all the quantities for each item, and also provide a separate list for each product line? Let us examine one way to accomplish this using template modes. First, create a copy of transform-one.xslt and name it transform-two.xslt.

 

In the template match for the root node in this new file, we will apply another template for the <item> nodes, placing HTML heading tags before the call to each template rule:

 

<xsl:template match="/">

   <html>

     <head>

       <title>Transformation Example</title>

     </head>

     <body>

       <h1>Item Quantities</h1>

       <xsl:apply-templates select="//item" mode="QTY" />

       <h1>Item Product Lines</h1>

       <xsl:apply-templates select="//item" mode="PL" />

     </body>

   </html>

 </xsl:template>

 

The first <apply-templates> element for <item> nodes now contains an attribute named mode with a value of QTY. The second <apply-templates> element contains an identical XPath expression, "//item", but with a mode attribute value of PL.

 

Now let's see the template elements that will match each of these:

 

<xsl:template match="item" mode="PL">

   <div>

   Item=<xsl:value-of select="@id" />,

   Product Line=<xsl:value-of select="@productLine" />

   </div>

 </xsl:template>

 

 <xsl:template match="item" mode="QTY">

   <div>

   Item=<xsl:value-of select="@id" />,

   Quantity=<xsl:value-of select="quantity" />

   </div>

 </xsl:template>

 

Notice that we're declaring the templates in the opposite order to which they are called. We can do this because of the declarative nature of the language. Each template element has a mode attribute which corresponds to the calling <apply-templates> element. This mechanism allows us to differentiate matches on the same node, and trigger different template rules as we wish.

 

Now type the following URL into your browser:

 

http://localhost/transforms/transformxml.aspx?xml=shopping-cart.xml&xslt=transform-two.xslt

 

The value for xslt in the query string is now transform-two.xslt. This example also demonstrates the reusability of the TransformXML.aspx page, which we haven't had to recompile. The results appear as follows:

 

 

 

Named Templates

We also have the ability to apply a template by name, rather than by node match. When using this technique, the context node of the calling template becomes the context node of the named template. This allows us to call a specific template from any template in the style sheet. So, as the mirror image of the previous example, we will now apply one transformation to two separate node types.

 

Add the following named template to transform-two.xslt:

 

<xsl:template name="BoldRed">

   <span style="color:#FF0000;font-weight:Bold">

     <xsl:value-of select="." />

   </span>

 </xsl:template>

 

Note the name attribute in place of the match attribute. As its name suggests, this template wraps the text value of the context node in a <span> HTML element with a style attribute set to make the content bold and red.

 

Now let's add two more templates, matching the productLine attribute and quantity element respectively:

 

<xsl:template match="@productLine">

   <xsl:call-template name="BoldRed" />

 </xsl:template>

 <xsl:template match="quantity">

   <xsl:call-template name="BoldRed" />

 </xsl:template>

 

Both these templates call the same "BoldRed" template with the <call-template> syntax. Should we now make any changes to the "BoldRed" named template, they will apply to all calling templates. The final change is to apply the above two templates from the other templates, like so:

 

<xsl:template match="item" mode="PL">

   <div>

     Item=<xsl:value-of select="@id" />,

     Product Line=<xsl:apply-templates select="@productLine" />

   </div>

 </xsl:template>

 <xsl:template match="item" mode="QTY">

   <div>

     Item=<xsl:value-of select="@id" />,

     Quantity=<xsl:apply-templates select="quantity" />

   </div>

 </xsl:template>

 

 

When the page is executed with these changes, the result is as follows:

 

 

Here, the values for the <quantity> elements and productLine attributes are red and bold (trust me on this!). We can now do this for any other element, by simply calling the named template to handle the element in question.

Controlling Document Output

Now that we have a basic understanding of the purpose of template rules in a style sheet, we'll move on to investigate how to control the document output. We'll see how to sort the results of the transformation, and apply conditional logic to filter the result.

 

Sorting Node-Sets with <sort>

A common scenario when dealing with data is to provide it in a sorted order. XSLT provides the means for sorting node-sets in our style sheets with the <sort> element.

 

The <sort> element supports several attributes, as described in this table:

 

Attribute of <sort>

Description

select

An XPath expression using a relative location path to provide the value on which the sort is based. The default value is ".", which uses the context node.

lang

The language the sort is based on. If not specified, this is derived from the system environment.

data-type

Typically "text" or "number".Sorts on "text" are sorted alphabetically. Sorts on "number" convert the node to a numeric value and then sort the node- set numerically.

order

Either "ascending" or "descending". The default is "ascending", meaning lower values appear first.

case-order

Possible values are "upper-first" or "lower-first". This allows us to determine which comes first when a word or character begins with a different case. The default value is "upper-first".

 

The <sort> element can be used in one of two ways:

§          As a child of <apply-templates>

§          As a child of <for-each>

 

We will start by exploring how to sort using <apply-templates>.

 

Using <sort> with <apply-templates>

Remember, when we call <apply-templates>, the node-set resulting from the XPath expression in the select attribute is ordered as the nodes were found in the source document. If we want to override that behavior, we can place a <sort> element as a child of <apply-templates>.

 

Open the transform-two.xslt style sheet, and locate the template match for the root node. Add two <sort> elements as shown:

 

<xsl:template match="/">

   <html>

     <head>

       <title>Transformation Example</title>

     </head>

     <body>

       <h1>Item Quantities</h1>

       <xsl:apply-templates select="//item" mode="QTY">

         <xsl:sort

           select="quantity" data-type="number"

           order="descending"         />

       </xsl:apply-templates>

       <h1>Item Product Lines</h1>

       <xsl:apply-templates select="//item" mode="PL">

         <xsl:sort

           select="@id"

           data-type="text"

           order="descending"         />

       </xsl:apply-templates>

     </body>

   </html>

 </xsl:template>

 

Notice how we've changed both <apply-templates> elements so that they are no longer empty. Each now has a <sort> child element, with attributes specifying how to perform the sort. In our example, the results appear as follows:

 

 

Under the Item Quantities heading, the nodes are now listed in descending order based on the value in the <quantity> element, whereas the nodes listed under the Item Product Lines header are listed in descending order according to the value of the id attribute on the <item> element.

 

Performing Repetition with <for-each>

When dealing with symmetrical data, we may elect to iterate through each node in a node-set. In XSLT, this is accomplished using the <for-each> element. As the name implies, it applies the same processing logic to each node in a node-set.

 

Creating an HTML Table

Still using the shopping-cart.xml file as our data source, let's create a new XSLT style sheet for it, named transform-three.xslt in the same directory as our Transforms project.

 

This all-new style sheet starts off like this:

 

<xsl:template match="/">

   <html>

     <head>

       <title>Transformation Example</title>

     </head>

     <body>

       <h2>Shopping Cart Items</h2>

       <table style="border: thick solid;">

         <tr style="background-color:#000000;color:#FFFFFF">

           <th>Line Number</th>

           <th>Item Number</th>

           <th>Description</th>

           <th align="right">Quantity</th>

           <th align="right">List Price</th>

         </tr>

 

So far, we've got some literal HTML markup in the template match for the root node to format our result. We have an opening <table> tag, followed by the column headings.

 

In our transformation, we want a <tr> row for each item in the document, which we will create with the <for-each> element:

<xsl:for-each select="//item">

   <tr>

     <td><xsl:value-of select="position()" /></td>

     <td><xsl:value-of select="@id" /></td>

     <td><xsl:value-of select="description" /></td>

     <td align="right"><xsl:value-of select="quantity" /></td>

     <td align="right"><xsl:value-of select="list-price" /></td>

   </tr>

 </xsl:for-each>

 

When the XSLT processor reaches the <xsl:for-each> element above, it establishes a node-set of <item> elements in the same order as in the source XML. Inside the <xsl:for-each> element, each <item> element becomes the context node for any XPath expressions. The first <td> tag of the row represents the line number, which is generated by calling the position() XPath function to give the position of the context node in the node-set.

 

Finally, we close the <table> and other HTML tags:

 

</table>

     </body>

   </html>

 </xsl:template>

 

 </xsl:stylesheet>

 

We have only one template match in our style sheet. However, with the use of <for-each>, we were able to process each <item> node in the document.

 

Type the following URL in the browser to see the results:

 

http://localhost/transforms/transformxml.aspx?xml=shopping-cart.xml&xslt=transform-three.xslt

 

The resulting screen will be something like this:

 

 

 

Using <sort> with <for-each>

As we mentioned earlier, we can use the XSLT <sort> element within a <for-each> element to sort the document output.

 

In our example, we might want the items sorted in descending order by list price. We can do this simply by adding the following element as an immediate child of our <for-each>:

 

<xsl:for-each select="//item">

   <xsl:sort select="list-price"

     data-type="number"

     order="descending"

   />

 

Calling the previous URL now displays this screen:

 

 

 

Notice that there was no change to the line number values as produced by the XPath position() function. This is because the sort is executed on the node-set first. Keep in mind therefore that using the position() function after sorting the node-set will not return the position of the node as it originally appears in the source XML document.

 

Using Conditional Logic in XSLT

XSLT is a programming language, and as such it has elements that provide the functionality of the if statement. These elements control document output according to Boolean tests, and they are:

 

§          <if>

§          <choose>..<when>..<otherwise>

 

The <if> and <when> elements have a test attribute, where we provide a Boolean expression. Since we can provide XPath expressions for this attribute, the following table shows how the results are converted to either true or false:

 

Expression Data-Type

Boolean Result

Boolean

The result is true or false based on the Boolean expression.

For example:

        5 > 8

The result of this Boolean expression is false.

String

If the string is empty, the result is false, otherwise true.

Numeric

If the number is zero, the result is false, otherwise true.

For example:

              -321

This is interpreted as true.

Node-Set

If the node-set is empty, the result is false, otherwise true.

The expression in the test attribute behaves identically to the argument for the XPath boolean() function.

 

Controlling Output using <if>

Let's add a few lines to our transform-three.xslt style sheet to demonstrate how these elements let us easily control document output. We will add a new column to the HTML <table>, which will contain special information if the product line is 2:

 

<table style="border: thick solid;">

   <tr style="background-color:#000000;color:#FFFFFF">

     <th>Line Number</th>

     <th>Item Number</th>

     <th>Description</th>

     <th align="right">Quantity</th>

     <th align="right">List Price</th>

     <th>Notes</th>

   </tr>

   <xsl:for-each select="//item">

     <xsl:sort  select="list-price"

       data-type="number"

       order="descending"     />

     <tr>

       <td><xsl:value-of select="position()" /></td>

       <td><xsl:value-of select="@id" /></td>

       <td><xsl:value-of select="description" /></td>

       <td align="right"><xsl:value-of select="quantity" /></td>

       <td align="right"><xsl:value-of select="list-price" /></td>

       <td>

         <xsl:if test="@productLine='2'" >

           Call for details!

         </xsl:if>

       </td>

     </tr>

   </xsl:for-each>

 </table>

 

The test condition in the XSLT <if> element checks to see if the current item's product line is equal to the number 2. If the result is true, the message Call for details! will be output on that row. Here is how the resulting screen looks:

 

 

Controlling Output using <choose>..<when>..<otherwise>

Much like other languages, XSLT allows us to check for multiple conditions in one logical block. This is similar to switch..case in C# and Select..Case in VB.NET. The syntax is as follows:

 

<choose>

   <when test="Boolean expression">

     [Output 1]

   </when>

   <when test="Boolean expression">

     [Output 2]

   </when>

   <otherwise>

     [Output 3]

   </otherwise>

 </choose>

 

The XSLT processor checks each <when> child of the <choose> element for an expression that returns true. If the processor finds a true result, the output is rendered and the <choose> is escaped. If no <when> test returns true, then the processor uses the output given in the <otherwise> element, if present. Thus a <choose> with only one <when> and an <otherwise> is similar to using an if..else statement.

 

Let's add a <choose> to our transform-three.xslt file. Our aim is to create a different background for alternating items in the table:

 

<table style="border: thick solid;">

   <tr style="background-color:#000000;color:#FFFFFF">

     <th>Line Number</th>

     <th>Item Number</th>

     <th>Description</th>

     <th>Quantity</th>

     <th>List Price</th>

     <th>Notes</th>

   </tr>

   <xsl:for-each select="//item">

     <xsl:sort  select="list-price"

       data-type="number"

       order="descending"

     />

     <xsl:element name="tr">

       <xsl:attribute name="style">

         <xsl:choose>

           <xsl:when test="position() mod 2">

             background-color:#DDDDFF;

           </xsl:when>

           <xsl:otherwise>

             background-color:#BBBBBB;

           </xsl:otherwise>

         </xsl:choose>

       </xsl:attribute>

       <td><xsl:value-of select="position()" /></td>

       <td><xsl:value-of select="@id" /></td>

       <td><xsl:value-of select="description" /></td>

       <td align="right"><xsl:value-of select="quantity" /></td>

       <td align="right"><xsl:value-of select="list-price" /></td>

       <td>

         <xsl:if test="@productLine='2'" >

           Call for details!

         </xsl:if>

       </td>

     </xsl:element>

 </xsl:for-each>

 </table>

 

Now wait a minute! What is the <xsl:element> element that we just threw in there? And what is the <xsl:attribute> element? These are necessary in this example, as our XSLT here is building up the opening <tr> tag in the <table>. We will cover this more in just a moment.

 

For now, examine the test condition in our <when> element. It essentially checks to see whether the position of the <item> in the node-set is odd or even. If odd, the result is true, which executes the output in the <when> element. If false, the output in <otherwise> is used.

 

So, to come back to why we couldn't simplify the <choose> like this:

 

<xsl:choose>

   <xsl:when test="position() mod 2">

     <tr style="background-color:#DDDDFF;">

   </xsl:when>

 

   <xsl:otherwise>

     <tr style="background-color:#BBBBBB;">

   </xsl:otherwise>

 </xsl:choose>

 

The issue here becomes apparent if we view the above XML fragment through the eyes of an XML parser. The parser sees two opening <tr> tags, and other elements overlapping. This is not well-formed, the parser gets upset, and our style sheet will break. That is why the earlier solution uses the XSLT <element> and <attribute> elements to accomplish the task. Note that making <attribute> a child of <element> is how we define an attribute for that element, and that the closing </element> tag inserts the corresponding </tr> tag in this case. Alternatively, we could've used <xsl:text> elements, which can produce the same output produced by <xsl:element> and <xsl:attribute>, as long as we specify the disable-output-escaping="yes" attribute to output the angle- brackets correctly.

Understanding XSLT Functions, Variables, and Parameters

The XSLT language supports a number of functions to provide, among other things, extended support for node-set operations. We will not be able to cover every XSLT function in this section, so we will instead highlight just those functions that can be very useful in a web environment.

 

Uniquely Identifying Nodes with generate-id()

We often need a way to uniquely identify nodes in the output document. We may need a unique value for an ID attribute, or to differentiate two or more nodes with the same name and value. The generate-id() function gives us this ability.

 

Save transform-three.xslt as transform-four.xslt in our Transforms web application. In transform-four.xslt, we'll add another column to our table, which will contain a button to display the generated ID for each <item> node.

 

In the <tr> tag holding table heading information, add a column named ID:

 

<tr style="background-color:#000000;color:#FFFFFF">

   <th>Line Number</th>

   <th>ID</th>

   <th>Item Number</th>

   <th>Description</th>

   <th>Quantity</th>

   <th>List Price</th>

   <th>Notes</th>

 </tr>

 

Then, in the <for-each> element, add the code highlighted below:

 

<xsl:for-each select="//item">

   <xsl:sort  select="list-price"

     data-type="number"

     order="descending"

   />

   <xsl:element name="tr">

     <xsl:attribute name="style">

       <xsl:choose>

         <xsl:when test="position() mod 2">

           background-color:#DDDDFF;

         </xsl:when>

         <xsl:otherwise>

           background-color:#BBBBBB;

         </xsl:otherwise>

       </xsl:choose>

     </xsl:attribute>

     <td><xsl:value-of select="position()" /></td>

     <td>

       <input  type="button"

           id="{generate-id()}"

           onclick="alert(this.id);"

           value="Click!" />

     </td>

     <td><xsl:value-of select="@id" /></td>

     <td><xsl:value-of select="description" /></td>

     <td align="right"><xsl:value-of select="quantity" /></td>

     <td align="right"><xsl:value-of select="list-price" /></td>

     <td>

       <xsl:if test="@productLine='2'" >

         Call for details!

       </xsl:if>

     </td>

   </xsl:element>

 </xsl:for-each>

 

Here, we have added an HTML <input> tag of type button. Notice the syntax for the id attribute. enclosing the function in curly braces ({}) produces the same effect as calling an <xsl:value-of> statement right inside the quotes. The function returns a string that uniquely identifies the context node, which in this case is the current <item> element. The onclick attribute calls the Javascript alert function to open a message box with the value of the unique ID.

 

Save the changes to transform-four.xslt, and specify it in the query string for the TransformXML.aspx page. The results look like this:

 

 

If we click the first button, we get a message box something like this:

 

 

Each button will of course return a different value. The method used to generate the ID is dependent on the implementation, and cannot be relied upon to always produce the same value for the same document, even with the same transform engine.

 

We will use this function again in the next example to compare nodes with each other.

 

Accessing Nodes using <key> and key()

A common situation I have faced when using XSLT style sheets is the need to transform data items according to the categories they are classed under in that particular XML dialect. The real challenge is to do so dynamically, without hard-coding template rules for each category. By using the XSLT <key> element and key() function, we can retrieve such "category" nodes in a document prior to processing the template rules, and then use the acquired node-set for access later.

 

Understanding the <key> Element

The <key> element is a top-level element, which means it must be a child of the <stylesheet> element. There are three attributes of <key>:

 

§          name – Used as an identifier in the XSLT style sheet. Typically referenced by the key() function.

 

§          match – An XPath expression that identifies the nodes in the source XML document to be gathered into the node-set. This search is done throughout the document, so it is not necessary to use the // operator to find all nodes.

 

§          use – A relative location path identifying how to access the matched node. To access the node by its own value, supply the "." self expression. To access the matched node by a relative node (such as an attribute of the element), provide the respective location path. This is also referenced by the key() function. This will become clearer once we've looked at an example.

 

Understanding the key() Function

The key() function allows us to access the matched nodes identified by the XSLT <key> element. The function accepts two arguments:

 

§          name – This should match the name attribute of the corresponding <key> element

 

§          key – The value by which to access the node-set. This corresponds to the use attribute of the <key> element.

 

To demonstrate, we'll use the following XML document, distinct.xml:

 

<?xml version="1.0"?>

 <doc>

   <parent category="E">

     <child>1</child>

   </parent>

   <parent category="D">

     <child>2</child>

   </parent>

   <parent category="E">

     <child>3</child>

   </parent>

   <parent category="A">

     <child>4</child>

   </parent>

   <parent category="B">

     <child>5</child>

   </parent>

   <parent category="B">

     <child>6</child>

   </parent>

   <parent category="C">

     <child>7</child>

   </parent>

   <parent category="A">

     <child>8</child>

   </parent>

   <parent category="A">

     <child>9</child>

   </parent>

   <parent category="B">

     <child>10</child>

   </parent>

 </doc>

 

Now, say that we want to display the value of each child listed according to its parent's category. If we know all the possible category values, we could create a separate template rule for each one. But there is a more dynamic and robust way which uses the XSLT <key> element and key() function.

 

Create a new XSLT style sheet in our Transforms project, and name it transform-five.xslt. This style sheet will be used to transform the distinct.xml document listed above.

 

Begin the style sheet with the following markup:

 

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"

 version="1.0">

   <xsl:key name="category_key" match="parent" use="@category" />

 

We have defined an XSLT <key> element, and named it category_key. The match attribute contains the single value of parent. By supplying this value, the XSLT processor will search the entire source document node by node for all occurrences of <parent> elements, and build a node-set accordingly. The use attribute informs the processor that the node-set is to be accessed by providing the value of the category attribute.

 

We now create a template match for the root node:

 

<xsl:template match="/">

   <html>

     <head>

       <title>Transformation Example</title>

     </head>

     <body>

       <xsl:apply-templates select="doc"/>

     </body>

   </html>

 </xsl:template>

 

The above template formats the output as an HTML document. The content of the <body> tag is provided by the template matching the <doc> document element.

 

Now it gets interesting. This is the template matching <doc>:

 

<xsl:template match="doc">

   <xsl:for-each select="parent[generate-id(.)=

    generate-id(key('category_key', @category)[1])]">

     <xsl:sort select="@category" />

     <div style="font-weight:bold">

       Category=<xsl:value-of select="@category" />

     </div>

     <xsl:for-each select="key('category_key', @category)">

       <xsl:sort select="child"

                 order="descending"

                 data-type="number" />

       Child=<xsl:value-of select="child" /><br />

     </xsl:for-each><br />

   </xsl:for-each>

 </xsl:template>

 

The magic really happens in the first <for-each> element. The XPath expression looks a little tricky, and I'll explain it as best I can. The first location step searches for <parent> elements that are children of the <doc> element (as this is the context node in this template). The predicate then selects only the <parent> element whose uniquely generated ID matches the first node returned from the category_key node-set containing the same generated ID.

 

The result is that this <for-each> neatly collects together <parent> nodes with distinct values in their category attribute.

 

The inner <for-each> statement then iterates through each child of each <parent> element, listing them in descending order.

 

The last thing to do is close the style sheet:

 

</xsl:stylesheet>

 

Save the transform-five.xslt sheet, and feed it and the distinct.xml file into the TransformXML.aspx page in your browser. The results are shown in the following screenshot:

 

 

In this way, we have dynamically categorized our output. Experiment by adding or removing categories in the distinct.xml document – the style sheet will react accordingly.

 

Assigning Values with <variable>

We can assign values to use in XPath expressions with the XSLT <variable> element. An XSLT variable's scope is limited to the element that it is a child of, so if declared as a top-level element, it can be accessed throughout the style sheet. The element has just two attributes:

 

§          name – Supplies the identifier for the variable, within the scope of its declaration

 

§          select – An optional XPath expression returning the value to be assigned to the variable

 

Alternatively, we can specify the value of the variable by placing content directly in the element. If select is not provided, and the element is empty, the variable equals an empty string.

 

Here is a simple demonstration of how to use a variable. In the transform-five.xslt style sheet, locate the template match for <doc>, and make the changes highlighted below:

 

<xsl:template match="doc">

   <xsl:for-each select="parent[generate-id(.)=

    generate-id(key('category_key', @category)[1])]">

     <xsl:sort select="@category" />

     <xsl:variable name="CategoryName" select="@category" />

     <div style="font-weight:bold">

       Category=<xsl:value-of select="$CategoryName" />

     </div>

     <xsl:for-each select="key('category_key', @category)">

       <xsl:sort   select="child"

             order="descending"

             data-type="number" />

       Child=<xsl:value-of select="child" /><br />

     </xsl:for-each>

     <br />

   </xsl:for-each>

 </xsl:template>

 

In this example, we have assigned the category attribute to the CategoryName variable. For each iteration, the variable value changes. Note the syntax to access the variable in the line that follows shortly after the variable declaration:

 

Category=<xsl:value-of select="$CategoryName" />

 

The $ character denotes a variable identifier.

 

This example does not change the behavior of our transformation, and may seem a little pointless. Variables can really save us a lot of trouble when used to hold complex XPath expressions that are employed several times in the same template. In following examples though, we will see another practical use of this feature: to hold a complete XML document.

Processing Multiple XML Documents with document()

There are times when data in one XML document directly relates to data in another, and in order to create meaningful output, we must take values from one XML document and relate them to the other. Linking such documents together is made possible with the document() function, which could be thought of as a SQL JOIN between the two documents.

 

Although there are other uses for document(), the following example focuses on how to use it to link data between XML documents during a transformation.

 

We will link the distinct.xml document to the following document, called categories.xml:

 

<?xml version="1.0"?>

 <categories>

   <category id="A">ASP.NET</category>

   <category id="B">VB.NET</category>

   <category id="C">C#</category>

   <category id="D">ADO.NET</category>

 </categories>

 

Open the transform-five.xslt style sheet, and add the following variable declaration as a top- level element:

 

<xsl:variable name="categories_doc" select="document('categories.xml')" />

 

This associates the entire categories.xml document with the variable categories_doc. Now make these changes to the template for <doc>:

 

<xsl:template match="doc">

   <xsl:for-each select="parent[generate-id(.)=

    generate-id(key('category_key', @category)[1])]">

 

     <xsl:sort select="@category" />

 

     <xsl:variable name="CategoryName" select="@category" />

 

     <div style="font-weight:bold">

       Category=<xsl:value-of select="$CategoryName" />,

         <xsl:choose>

           <xsl:when test="$categories_doc//category[@id=$CategoryName]">

             <xsl:value-of select="$categories_doc//category[@id=

                                   $CategoryName]" />

           </xsl:when>

           <xsl:otherwise>

             Unknown

           </xsl:otherwise>

         </xsl:choose>

     </div>

     <xsl:for-each select="key('category_key', @category)">

       <xsl:sort   select="child"

             order="descending"

             data-type="number" />

       Child=<xsl:value-of select="child" /><br />

     </xsl:for-each>

     <br />

   </xsl:for-each>

 </xsl:template>

 

This new XSLT <choose> element contains a <when> element that tests for the existence of a <category> element in the categories.xml document matching the value currently in our CategoryName variable. If the test returns true, we select the text value of the matching <category> element. Otherwise, we output Unknown to indicate there was no match in the other XML document.

 

Save the changes to transform-five.xslt, and view the results in your browser. It should look like the following screenshot:

 

 

As we can see, the values from the categories.xml document are included in the output of the transformation. When there was no match for "E", the conditional logic output Unknown as we expect.

 

Creating Dynamic Output with <param>

Very similar to <variable> is the <param> element, as both can store information that we can access in our style sheet. One of the key differences is that it allows external data to be passed in when the transformation is invoked. Again, there are multiple uses for <param>, but in the following example we are going to focus on how to pass external data into the transformation to filter the results.

 

First, I'll just briefly mention the other important use for <param> elements: defining parameters for named templates, like so:

 

<xsl:template name="templateName">

   <xsl:param name="firstParameter" />

   <!-- Other parameters here -->

 </xsl:template>

 

We then pass in parameters by setting values with <with-param> child elements of the <call- templates> element:

 

<xsl:call-template name="templateName">

   <xsl:with-param name="firstParameter" select "XPathExpression" />

   <!-- Other parameters defined here -->

 </xsl:call-template>

 

These aren't something we would normally change programmatically, so we'll pay more attention to the use of parameters for style sheets.

 

Similarly to named templates, parameters are declared as immediate children of the <stylesheet> element (in fact, named templates can be thought of as localized inline style sheets), so add the following as a top level element of the transform-five.xslt sheet:

 

<xsl:param name="CategoryParam" />

 

Then in the template match for <doc>, incorporate an <xsl:if> element as shown below:

 

<xsl:template match="doc">

   <xsl:for-each select="parent[generate-id(.)=

    generate-id(key('category_key', @category)[1])]">

 

     <xsl:sort select="@category" />

 

     <xsl:variable name="CategoryName" select="@category" />

 

     <xsl:if test="not($CategoryParam) or $CategoryName=$CategoryParam">

       <div style="font-weight:bold">

         Category=<xsl:value-of select="$CategoryName" />,

         <xsl:choose>

             <xsl:when test="$categories_doc//category[@id=$CategoryName]">

<xsl:value-of select="$categories_doc//category[@id=$CategoryName]"               />

             </xsl:when>

             <xsl:otherwise>

               Unknown

             </xsl:otherwise>

           </xsl:choose>

       </div>

 

       <xsl:for-each select="key('category_key', @category)">

         <xsl:sort   select="child"

               order="descending"

               data-type="number" />

         Child=<xsl:value-of select="child" />

         <br />

       </xsl:for-each><br />

     </xsl:if>

   </xsl:for-each>

 </xsl:template>

The <if> element does a clever trick here. It tests two conditions using the parameter (which is identified with $ just like a variable). The first condition determines if $CategoryParam has no value, and the second determines if the $CategoryParam value is equal to $CategoryName. If either is true, the contents of the <if> element are processed.

 

So now we just need to know how to provide a value for the $CategoryParam parameter from an ASPX page

 

Using XsltArgumentList to Add Parameters

We have to return to Visual Studio .NET, and modify our TransformXML.aspx.cs code to provide support for parameters. We will use the XsltArgumentList object, which we then pass to the Transform method of the XslTransform object. Make the following changes to our Page_Load event handler:

 

private void Page_Load(object sender, System.EventArgs e)

{

   // Put user code to initialize the page here

   try

   {

     string _XMLPath = Request.QueryString["xml"];

     string _XSLTPath = Request.QueryString["xslt"];

     string _Param = Request.QueryString["category"];

 

     // Instantiate and Load the XPathDocument

     XPathDocument _SourceXml =

       new XPathDocument( Server.MapPath(_XMLPath) );

 

     // Instantiate and load the XslTransform object

     XslTransform _Transform = new XslTransform();

 

     _Transform.Load( Server.MapPath(_XSLTPath));

 

     // Create the XsltArgumentList object

     XsltArgumentList _Args = null;

 

     if (_Param != null && _Param != String.Empty)

     {

       _Args = new XsltArgumentList();

       _Args.AddParam("CategoryParam", "", _Param);

     }

 

// Send result of transformation to Response.Output stream

    _Transform.Transform(_SourceXml, _Args, Response.Output );

   }

   catch (Exception handledException)

   {

     Response.Write(handledException.Message);

   }

 }

 

The first change above captures a possible value named category from the querystring. Our next change is the declaration of a new XsltArgumentList object. We initialize it to null, and only instantiate it if there is a value in the _Param string. If there is a value, we instantiate the object and call the AddParam method to set the value of the _Param string as the CategoryParam named parameter in the XSLT style sheet. (The middle argument in AddParam is for namespace support.)

 

If we now save and rebuild the project, we can call the TransformXML.aspx page with the distinct.xml and transform-five.xslt files in the querystring. This gives the same results as before. Now append the querystring with the following:

 

&category=B

 

This will pass the value of "B" to our style sheet, to deliver the following results:

 

 

Try setting category to different values in the query string, and you'll see that the parameter filters the output to display only the matching category.

 

Summary

In this chapter, we learned how to utilize XSLT style sheets in our web applications. We covered the various reasons why and when we would use XSLT. We looked at language syntax, template rules, and saw how XPath is used throughout. We also explored how it allows us to transform data dynamically using conditional statements and XSLT functions. We looked at some typical situations we may confront during our development careers, and how XSLT provides a neat solution.

 

However, the examples in this chapter have used static XML documents as source data. In many cases, the XML we use may either be generated from another component, or derived from a data source such as SQL Server or MS Access. In our next chapter, we will see how to use ADO.NET and its XML features to provide a dynamic data source for our application. Along the way, we'll see an example or two of how to use XSLT against such data sources.

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