perfectxml.com
 Basic Search  Advanced Search   
Topics Resources Free Library Software XML News About Us
You are here: home »» Free Library »» Wrox Press » Professional ASP.NET 1.0 XML with C# Saturday, 13 October 2007

Page: 1   |   2   |   3   |   4   |   5   |   6        

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.

Page: 1   |   2   |   3   |   4   |   5   |   6        

Next Page >>>        

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