Reader Level:
ARTICLE

Changing XSL StyleSheets on the FLY in C# and ASP.NET

Posted by Mike Gold Articles | Visual C# September 26, 2006
This article shows you how you can use alter XSL in memory to transform XML data into HTML with different results.
  • 0
  • 0
  • 50684
Download Files:
 

Edited by Nina Miller





Figure 1 - XSL Transforming an XML Data File based on Radio Selection

XSL (eXtensible Stylesheet Language) is a powerful XML language that enables you to specify how you want your XML data to appear on your Web Page. Table 1 shows a list of common XSL commands and what they do:

Command Description Example
<xsl:template match="xpath expr">
 
 
 
used to match a node in an xml document <xsl:template match="/">
<xsl:apply-templates/> used to substitute the contents of a template with the value contained in an xml node <xsl:template match="date">
<p><font face="verdana" size="1"><xsl:apply-templates/></font></p>
</xsl:template>
<xsl:value-of select="node expr"/> Outputs the value of the matching node in the context based on the select value <xsl:value-of select="."/>
<xsl:for-each select="loop node"> Allows you to loop through a node set and output a filled template for each node <xsl:for-each select="Customers/Customer">
<TR>
    <TD><xsl:value-of select="Name"/></TD>
    <TD><xsl:value-of select="Address"/></TD>
</TR>
<xsl:attribute  name="attribute name"> used to add an xml attribute to an element in the final output
<img>
<xsl:attribute name="src">
<xsl:value-of select="@src" />
</xsl:attribute>         
</img>
The output:					
<img src="myimage.gif">     	
<xsl:if test=boolean expression> used to decide whether or not to include the transformed xml data in the output <xsl:if test="price > 0">
             <font color="red"><xsl:value-of select="product"/></font><br/>
</xsl:if> 

<xsl:choose>
    <xsl:when test=boolean expression> 
        template body
    </xsl:when>
    <xsl:otherwise>
        template body
    </xsl:otherwise>
</xsl:choose>

 
 
similar to a select statement.  used to decide which template should be used to generate the output
 <xsl:choose>
<xsl:when test="price &gt; 10">
<td bgcolor="#ff00ff">
<xsl:value-of select="product"/></td>
</xsl:when> <xsl:otherwise>
<td><xsl:value-of select="product"/></td>
</xsl:otherwise> </xsl:choose>
 
 
<xsl:sort select="node name" data-type="node data type" /> sorts the output in a for-each loop or in an 
apply-templates
<xsl:for-each select = "song">

<xsl:sort select=
"listprice"
data-type=
"number" />
<xsl:variable name="var name" select="var value" /> variables allow you to define a substitution value which you can use throughout your xsl transform.  Variables are used by preceding them with a $
  <xsl:variable name="sport" select="/sports/sport" />

<xsl:for-each select="$sport/soccer" >

Table 1 - Some XSL Commands

In our ASP.NET application we want to take a list of customers and display them nicely on a web page. The XSL code in listing 1a cycles through each customer in the XML customer data shown in listing 1b and transforms the information into a readable HTML Table (listing 2). The transform uses the XSL for-each select command to loop and builds the table row by row. Within the loop, the transform fills the appropriate customer information using the XSL value-of select command, selecting on each Customer node in the XML file.

Listing 1 a

<?xml version="1.0" encoding="utf-8"?>
<
xsl:stylesheet version="1.0xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
<
xsl:template match="/"
>
<
html
>
  <
body
>
  <!--
This code cycles through each customer in the xml data
    And lists them in a different row in an html table
-->
<
h2>Customers of Flying Cows Company</h2
>
<
table border="1"
>
     <
tr bgcolor="#9acd32"
>
        <
th align="left">full name</th
>
        <
th align="left">e-mail</th
>
        <
th align="left">phone</th
>
    </
tr
>
 <
xsl:for-each select="Customers/Customer"
>
  <
tr
>
   <
td
>
      <
xsl:value-of select="LastName"/>,

      <
xsl:value-of select="FirstName"/>
   </
td
>
    <
td
>
       <
xsl:value-of select="Email"
/>
   </
td
>
   <
td
>
     <
xsl:value-of select="Phone"
/>
   </
td
>
 </
tr
>
</
xsl:for-each
>
</
table
>
</
body
>
</
html
>
</
xsl:template
>
</
xsl:stylesheet>

Listing 1 b

<?xml version="1.0" encoding="utf-8" ?>
<
Customers
>
  <
Customer
>
    <
FirstName>Fred</FirstName
>
    <
LastName>Smith</LastName
>
    <
Email>
fred@aol.com</Email
>
    <
Phone>212-555-1350</Phone
>
    <
Employee>yes</Employee
>
  </
Customer
>
  <
Customer
>
    <
FirstName>Bill</FirstName
>
    <
LastName>Jones</LastName
>
    <
Email>
jonesb@aol.com</Email
>
    <
Phone>212-555-1450</Phone
>
    <
Employee>no</Employee
>
  </
Customer
>
  <
Customer
>
    <
FirstName>Ned</FirstName
>
    <
LastName>Johnson</LastName
>
    <
Email>
nedmeister@aol.com</Email
>
    <
Phone>212-555-1351</Phone
>
    <
Employee>no</Employee
>
   </
Customer
>
</
Customers>

 
 

If the XSL transform in listing 1a is applied to the XML Data in listing 1b, the HTML table output in listing 2 results:

Listing 2 - Output resulting from the xsl transform of listing 1a applied to xml data in listing 1b

 <html>
<
body
>
  <
h2>Customers of Flying Cows Company</h2
>
   <
table border="1"
>
     <
tr bgcolor="#9acd32"
>
        <
th align="left">full name</th
>
        <
th align="left">e-mail</th
>
        <
th align="left">phone</th
>
    </
tr
>
    <
tr
>
         <
td> Smith,Fred </td
>
         <
td>
fred@aol.com</td
>
         <
td>212-555-1350</td
>
       </
tr
>
       <
tr
>
          <
td> Jones,Bill </td
>
          <
td>
jonesb@aol.com</td
>
          <
td>212-555-1450</td
>
      </
tr
>
      <
tr
>
          <
td>Johnson,Ned</td
>
          <
td>
nedmeister@aol.com</td
>
          <
td>212-555-1351</td
>
       </
tr
>
     </
table
>
   </
body
>
   </
html>

Using ASP.NET to Transform XML

ASP.NET comes with an XML control that allows you to transform an XML file using XSL. You just need to do 2 things: (1) assign an XmlDocument containing the XML you want to transform to the XML control's Document property.  (2) assign an XslTransform class containing the XSL to the Transform property of the XML control.

Listing 3 - Making the transform happen using the Xml class

// make the xsl transform happen

XmlCustomerTable.Document = xmlDoc;

XmlCustomerTable.Transform = xslTransform;

The only tricky part is stuffing the XML into the XML Document and stuffing the XSLT into the Transform object. Reading the XML is fairly straightfoward using the XMLDocument class as shown in listing  4.

Listing 4 - Reading the customers.xml file into the XmlDocument class

//  get the physical directory path of the root default page's directory

string path = Path.GetDirectoryName(Request.PhysicalPath);

// Create the Xml Document

XmlDocument xmlDoc = new XmlDocument();

// load the xml customer database from the customers.xml file

xmlDoc.Load(String.Format("{0}/Docs/{1}", path, "customers.xml"));

If we just wanted to use the existing XSL contained in the customerformat.xsl file, then the process of retrieving the XSL is fairly straightforward. Simply construct the XslTransform object and call the Load method, just like we did with the XmlDocument.

Listing 5 - Loading a StyleSheet into an XslTransform object

// by loading it indirectly with an XmlReader
XslTransform xslTransform = new XslTransform
();
xslTransform.Load(String.Format("{0}/Docs/{1}",path, Session["TransformFile"]));

However, we would like to do something a bit more interesting. We want to be able to manipulate the XSL in memory.  Therefore we need a way to read the XSL, convert it to a string, alter it, and then shove the string into an XslTransform object. This turns out to be a little less straightforward. The XSL we want to load into memory is shown below in listing 6.  Note that this XSL differs from the XSL in listing 1a with just one xsl command, xsl:if.  We want to be able to dynamically replace the expression <xsl:if test="{0}"> with a boolean expression of our choosing, such as <xsl:if test="Employee='yes'">.  In the case of our ASP.NET program, we want to replace the expression {0} with something that is always true (such as the boolean expression 1 > 0)  when Employees Only is not checked, but we want to filter on the condition 'Employee='yes'  when Employees Only is checked.  This way, all workers (consultants and employees) will be shown when the employees checkbox is not checked, but only employees will be shown when the employees check box is checked.

Listing 6 - XSL Transform with xsl:if to produce a table based on a given condition

// 

<?xml version="1.0" encoding="utf-8"?>
<
xsl:stylesheet version="1.0xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
<
xsl:template match="/"
>
<
html
>
  <
body
>
  <!--
This code cycles through each customer in the xml data
    And lists them in a different row in an html table
-->
<
h2>Customers of Flying Cows Company</h2
>
<
table border="1"
>
     <
tr bgcolor="#9acd32"
>
        <
th align="left">full name</th
>
        <
th align="left">e-mail</th
>
        <
th align="left">phone</th
>
    </
tr
>
 <
xsl:for-each select="Customers/Customer"
>

<
xsl:if test="{0}"
>

  <
tr
>
   <
td
>
      <
xsl:value-of select="LastName"/>,

      <
xsl:value-of select="FirstName"/>
   </
td
>
    <
td
>
       <
xsl:value-of select="Email"
/>
   </
td
>
   <
td
>
     <
xsl:value-of select="Phone"
/>
   </
td
>
 </
tr
>
</
xsl:for-each
>
</
table
>
</
body
>
</
html
>
</
xsl:template>

</xsl:if>

</
xsl:stylesheet>

 
 
 
 

In order to read the XSL into a string that we can manipulate in memory we will use a StreamReader. Then we'll replace the {0} in the string with the appropriate boolean expression using the string Replace method. Next we'll read the string into a memory stream and then use the memory stream to construct an XmlTextReader containing the string. Finally, we will construct the XslTransform object with the XmlTextReader containing the manipulated XSL string. As convoluted as this sounds, it is a workable approach. However, it would have been faster if there were a XslTransform method you could call that took a string containing the XSL.

Listing 7 - Dynamically changing XSL inside a string and loading the string into and XslTransform object

// need to go through a bit of a convoluted task
// to load the transform with a string
// by loading it indirectly with an XmlReader

XslTransform xslTransform = new XslTransform();
xslTransform.Load(String.Format("{0}/Docs/{1}",path, Session["TransformFile"
]));

StreamReader sr = new StreamReader(String.Format("{0}/Docs/{1}", path, Session["TransformFile"
]));
string
xslBlock = sr.ReadToEnd();
sr.Close();

// alter the 'xsl:if' statement in the xsl string
// to test for filtering on employees
if
(chkEmployee.Checked)
 {
  
// if 'only employees' is checked,
   // put a condition to test whether or not
   // we have an employee
  // in the xsl:if expression

   xslBlock = xslBlock.Replace("{0}", "Employee = \'yes\'"
);
 }
 else

  {
   
// if 'only employees' is not checked,
    // put a condition that is always true (1>0)
    // in the xsl:if expression

    xslBlock = xslBlock.Replace("{0}", "1 &gt; 0"
);
  }

// need to convert the xsl string to a memory stream
// in order to put it in the xml reader.
 
MemoryStream ms = new MemoryStream(System.Text.ASCIIEncoding
.ASCII.GetBytes(xslBlock));
  XmlReader reader = new XmlTextReader
(ms);
 
  // load the tranform with the XmlReader containing
  // the xsl transform string

   xslTransform.Load(reader);

Conclusion

Although XSL may seem complex, it is actually a useful language for taking an XML file containing data and presenting it effectively on a web page. This article has shown you how you can take advantage of the features in ASP.NET to alter XSL on the fly in response to input from the web browser and display your XML data differently. So add a bit of style to your ASP.NET apps, to C a sharper view of your data with a combination of stylesheets and .NET. 
 

COMMENT USING