Performing XSLT Transforms Programmatically

Early in this chapter, I promised to show you how to perform server-side transforms programmatically after you learned more about the System.Xml namespace. At this point, you should have at least a tenuous grasp on the various types of DOM objects, how to read XML data, how to find nodes, and how to extract XML from a database. You've also seen how to use the Xmi Web server control to perform a transform. Now it's time to use some of the objects you've seen to perform one yourself.

Here's the basic procedure:

1. Load the XML into an XmiDocument, XPathDocument, or XpathNavigator.

2. Load the stylesheet into an XsiTransform instance.

3. Call the XsiTransform.Transform method.

The Transform method is overloaded. It can do the following:

■ Send the result of the transform to a TextWriter, Stream, or XmiWriter that you supply as a parameter.

■ Write the result of the transform to a file.

■ Return an XmiTextReader object that you can use to process the result.

Tip The .NET documentation states that you should use an XPathDocument object when possible because it is optimized for XSLT transformations.

The Web Form chl5-5.aspx contains an example that's actually a little more complex than it needs to be at this point because it uses client-side script, but it provides a good XSLT sorting example in addition to a simple programmatic server-side transform. You'll see more information about client-side scripting in Chapter 20, "Leveraging Browser Clients."

The example reads an XML document named peopie.xmi and an XSLT stylesheet named peopie.xsi. The peopie.xmi file contains a last name, first name, telephone number, and e-mail address for several fictional people. Here's a sample:

<?xmi version="l.0"?> <peopie>

<person>

<iast>Jones</iast> <first>Bob</first> <tei>555-666-7777</tei> <emaii>[email protected]</emaii> </person> <person>

<iast>Jones</iast> <first>Fred</first> <tei>839-298-7328</tei> <emaii>[email protected]</emaii> </person>

<!-- more person tags here —> </peopie>

The stylesheet transforms the document into an HTML table, creating a row for each person and putting the four data items (iast, first, tei, and emaii) in separate columns. It also has two parameters named sortfieid and sortorder that control the way the stylesheet sorts and orders the data. Initially, the sortfieid parameter is set to iast, and the sortorder parameter is set to ascending, so the stylesheet sorts the people by last name in ascending order.

However, the user can change the sort order by clicking on a column header, which fires a client-side function called sort that sets a hidden form field to the title of the column the user clicked and then forces the default form (Forml) to submit to the server. The Page_Load event creates a string containing the client-side code and then "registers" the script with the page using the

RegisterCiient-ScriptBiock method.

private void Page Load(object sender, System.EventArgs e) {

String sort_script;

sort script = "<script type=\"text/javascript\" " + "language=\"javascript\">" + "function sort(column) {" + "document.getElementById(\"Form1\")" + ".sortfield.value=column;" +

"document.getElementById(\"Form1\").submit();}" + "</script>";

this.RegisterClientScriptBlock("sort script", sort script);

The server retrieves the name of the clicked column and uses it to set the stylesheet's sortfield parameter. In this example, the server simply alternates between sorting columns in ascending or descending order.

if (IsPostBack) {

mSortfield = Request.Form["sortfield"]; mSortorder = Request.Form["sortorder"]; Response.Write("Server sortorder=" + mSortorder +

"<br>"); if (mSortorder=="ascending") { mSortorder = "descending";

else if (mSortorder=="descending") { mSortorder="ascending";

Response.Write("ERROR: sortorder=" + mSortorder); Response.End();

The hidden input fields are a simple way to get the name of the clicked column posted to the server. The hidden fields aren't hard-coded into the HTML (.aspx) file; instead, the server creates the controls dynamically and sets their values. So that the form will post the values, you need to place the hidden input controls into the default server form. The Page object provides an automatic way to do that with the RegisterHiddenField method, which accepts two string arguments: the name of the hidden input and its initial value. The RegisterHiddenField method performs the equivalent of this series of steps:

// create a hidden input control

HtmllnputHidden hidden field = new HtmlInputHidden();

// find the default Form control and add the new control to its // Controls collection this.FindControl("Form1").Controls.Add(hidden_field);

// set the id and value for the new hidden input control hidden field.ID=someIDString; hidden field.Value=someValueString;

You can perform a similar series of steps to create any type of Web server or HTML control dynamically. The most common error people make while doing this is to add the control to the Page.Controls collection rather than the "Form1" form's Controls collection.

Because adding hidden input controls is such a common operation, you can accomplish the entire procedure in a single step:

this.RegisterHiddenField("someID", "someValue");

That's extremely convenient. Typically you would add controls in the Page_Load or the PreRender event. The RegisterHiddenField method renders like this:

<input type="hidden" name="someID" value="someValue" /> Listing 15.8 shows the code for the ch15-5.aspx.cs class.

Listing 15.8: Programmatic Server-Side XSLT Transform Example (chl5-5.aspx.cs)

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;

using System.Xml.Xsl;

using System.Xml.XPath;

namespace CSharpASP.chl5 {

/// Summary description for chl5 5. /// </summary>

public class chl5 5 : System.Web.UI.Page {

private void Page Load(object sender, System.EventArgs e) { String sort script; XPathDocument xml = null; XslTransform xslt = null; XsltArgumentList param = null; String mSortfield = "last"; String mSortorder = "ascending"; try {

if (IsPostBack) {

mSortfield = Request.Form["sortfield"]; mSortorder = Request.Form["sortorder"]; if (mSortorder=="ascending") { mSortorder = "descending";

else if (mSortorder=="descending") { mSortorder="ascending";

Response.Write("ERROR: sortorder=" +

mSortorder); Response.End();

xml = new XPathDocument(Server.MapPath(".") +

"\\people.xml"); xslt = new XslTransform();

xslt.Load(Server.MapPath(".") + "\\people.xsl"); this.RegisterHiddenField("sortorder", mSortorder); this.RegisterHiddenField("sortfield", mSortfield);

param = new XsltArgumentList(); param.AddParam("sortorder", "",mSortorder); param.AddParam("sortfield", "",mSortfield); xslt.Transform(xml, param,Response.OutputStream);

catch (XsltException exxslt) {

Response.Write("XsltException: " + exxslt.Message);

catch (XmlException exxml) {

Response.Write("XmlException: " + exxml.Message);

catch (Exception ex) {

Response.Write(ex.GetType().Name + ": " + ex.Message);

sort script = "<script type=\"text/javascript\" " + "language=\"javascript\">" + "function sort(column) {" + "document.getElementById(\"Form1\")." + "sortfield.value=column;" +

"document.getElementById(\"Form1\").submit();}" + "</script>"; this.RegisterClientScriptBlock("sort script", sort script); }"

// autogenerated code omitted

The XSLT stylesheet (see Listing 15.9, later in this chapter) has several interesting points. First, notice the two <xsl:parameter> tags defined near the top of the file, which have default initial values of last and ascending, respectively:

<xsl:param name="sortfield" select="last" /> <xsl:param name="sortorder" select="ascending" />

The stylesheet first selects the root node of the input XML document:

<xsl:template match="/">

So that you can see what's happening, the stylesheet then writes the current parameter values to the output:

Sorting by <xsl:value-of select="$sortfield"/><br/> Order = <xsl:value-of select="$sortorder"/><br/>

Next, it outputs a <table> tag and selects all the child nodes of the <people> tag in the input XML document, sorting them by the named parameter values. In XSLT, you reference variables by placing a dollar ($) sign in front of the name, for example $sortfield:

<table border="1" align="center">

<xsl:apply-templates select="people/child::node()[1]" mode="thead"/> <tbody>

<xsl:apply-templates select="/people/person"> <xsl:sort select="*[name(.) = $sortfield]" order="{$sortorder}"/> </xsl:apply-templates> </tbody> </table>

If you were to hard-code the sort field, you could write, for example:

The interesting point of the sort is that you can't simply substitute the parameter name:

<xsl:sort select="$sortfield" />

Instead, you have to select the nodes where the node name is equal to the sortfield parameter's value.

<xsl:sort select="*[name(.) = $sortfield]" />

The <xsl:sort> tag can accept an order attribute that can take either an ascending or descending value. The default is ascending. Again, you can't simply use the variable reference $sortorder because that references the parameter node itself, not its value. By placing the variable name in curly brackets, XSLT substitutes the text value of the node. Therefore, the complete sort tag is this:

<xsl:sort select="*[name(.) = $sortfield]" order="{$sortorder}"/>

Listing 15.9 shows the entire stylesheet.

Listing 15.9: The people.xsl XSLT Stylesheet Sorts the Contents of the people.xml File According to the sortfield and sortorder Parameter Values

<xsl:stylesheet xmlns:xsl="http://www.w3.org/l999/XSL/Transform" version="l.0">

<xsl:param name="sortfield" select="last" /> <xsl:param name="sortorder" select="ascending" /> <xsl:template match="/">

Sorting by <xsl:value-of select="$sortfield"/><br/> Order = <xsl:value-of select="$sortorder"/><br/> <table border="l" align="center">

<xsl:apply-templates select="people/child::node()[l]"

<xsl:apply-templates select="/people/person"> <xsl:sort select="*[name(.) = $sortfield]" order="{$sortorder}"/> </xsl:apply-templates> </tbody> </table> </xsl:template>

<xsl:template match="person"> <tr>

<xsl:for-each select="child::node()"> <td>

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

</xsl:for-each> </tr> </xsl:template>

<xsl:template match="person" mode="thead"> <thead><tr>

<xsl:for-each select="child::node()"> <th>

<xsl:attribute name="onclick">

sort('<xsl:value-of select="local-name()"/>');</xsl:attribute <xsl:attribute name="style">

cursor:hand</xsl:attribute> <xsl:value-of select="local-name()"/> </th> </xsl:for-each> </tr></thead> </xsl:template>

</xsl:stylesheet>

Figure 15.7 shows the output of the chl5-5.aspx Web Form running in a browser, sorted by first name in descending order.

Figure 15.7: The Web Form chl5-5.aspx output sorted by first name in descending order

Was this article helpful?

0 0

Post a comment