Page: 1 | 2 | 3 | 4 | 5 | 6
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.
|