XML Date Sorts

Create XSLT Extension Objects to Sort XML Data

Dan Wahlin

October 30, 2009

9 Min Read
ITPro Today logo

XtremeData

LANGUAGES: C#

ASP.NET VERSIONS:1.1

 

XML Date Sorts

Create XSLT Extension Objects to Sort XML Data

 

By Dan Wahlin

 

Extensible Stylesheet Language Transformations (XSLT)provides a flexible mechanism for sorting numbers and text found in XML documents.However, version 1.0 doesn't support sorting dates with the xsl:sort element.This is unfortunate, because dates are quite common in applications.

 

Consider the XML document shown in Figure 1. Usingstrictly native XSLT/XPath 1.0 features, it's difficult to sort the Customernodes based on dates contained in the CustomerSince child nodes, because thexsl:sort element's data-type attribute only supports text and numeric types. Inthis article, I'll demonstrate how you can get around this sorting issue(without resorting to multiple sorts based on date substrings) by using an XSLTextension object written in C#.

 

  

    ANTON

    ...

    2002-1-3

  

  

     ANATR

    ...

    2002-1-4

  

  

    AROUT

    ...

    2002-1-2

  

  ...

Figure 1: SampleXML document. It's commonly necessary to sort nodes in an XML document based ondates. The extension object demonstrated in this article allows sorting tooccur based on the CustomerSince node.

 

.NET XSLT extension objects are compiled classes thatprovide a powerful way to provide functionality otherwise not available in theXSLT language. By using extension objects, XSLT stylesheets can performadvanced math calculations, tie into data sources other than XML, and performmore common tasks, such as sorting dates.

 

Although extension objects have many advantages, they'renot without disadvantages. It's important to keep in mind that XSLT stylesheetsthat leverage extension objects are less portable across operating systems anddevelopment platforms. Of course, this isn't a problem if your stylesheetsdon't need to be ported to different platforms, but if portability is an issue,you may want to consider using specialized XSLT libraries, such as those foundat http://www.exslt.org.

 

XSLT Scripting

Before detailing how to build an extension object, let'stake a moment to discuss a viable alternative to extension objects referred toas XSLT scripting. Although it isn't part of the XSLT 1.0 specification, thistechnique involves embedding C#, VB .NET, or JScript code directly into an XSLTstylesheet. To help with performance, the embedded code is compiled into MSILthe first time it's executed.

 

The code in Figure 2 demonstrates how a C# function named ToDateStringcan be embedded in an msxsl:script element. The msxsl namespace prefixmust be defined within the XSLT stylesheet. It must also be associated with theurn:schemas-microsoft-com:xslt Uniform Resource Identifier (URI). The valuespecified in the implements-prefix attribute (the DateFuncnamespace prefix in this case) can be associated with any namespace URI.

 

      public string ToDateString(XPathNodeIterator node)    {      node.MoveNext();      return DateTime.Parse(        node.Current.Value).ToLongDateString();      }  ]]> Figure 2: XSLTstylesheets written to run within the .NET Framework can leverage script blocksas shown here. The ToDateString method converts dates such as 1-2-2002to a friendlier format, such as Wednesday, January 02, 2002.   Consider an example of calling the ToDateStringmethod from within an xsl:value-of element, to convert the data within theCustomerSince element to a friendlier date format:     select="DateFunc:ToDateString(CustomerSince)"/>   You can see that XSLT scripting makes it easy to extendstylesheet functionality with minimal effort. However, code placed in scriptblocks is only useful to the XSLT stylesheet in which it's embedded. It canalso be more tedious to write, especially when classes outside the System.Xmlassembly need to be used. Plus, you don't benefit from IntelliSense as youdevelop the code. (And let's face it: Many of us are addicted to IntelliSense.)Now that you've been introduced to XSLT scripts, let's move on and discuss theprocess of creating XSLT extension objects.   Sort Dates in XSLT with ExtensionObjects Writing XSLT extension object code is no different thanwriting code for any .NET application. Simply create a class with methods thatperform the activities you desire. No inheritance or interface implementationis required. To provide date sorting capabilities for XSLT stylesheets, Icreated a class named XSLTDateTime that has two methods: SortByDateand ToDateString.   Take a look at the complete code for SortByDate(see Figure 3). Notice that the method accepts three parameters: the contextnode being acted on by the XSLT processor, the node (or nodes) to return fromthe method after an XPath statement is executed, and the node containing thedate data on which the sort will be based.   publicXPathNodeIterator SortByDate(XPathNodeIterator node,    string nodeToSelect, stringdateNodeName) {   string xpath =     nodeToSelect +"[" + dateNodeName + " != '']";   // Check if we sort on an attribute or text node.    string sort = (dateNodeName.IndexOf("@") != -1)?     dateNodeName:dateNodeName + "/text()";   // Position nav on first node of node set.   node.MoveNext();  XPathNavigator nav =node.Current;   // Compile XPath expression so we can add a sort to it.   XPathExpression exp =nav.Compile(xpath);   DateComparerXSLT dc = new DateComparerXSLT();  exp.AddSort(sort,dc);   // Select nodes so we can see the sort.    return (XPathNodeIterator)nav.Select(exp); }Figure 3: The SortByDatemethod executes an XPath statement that returns one or more nodes sorted basedon dates. The method relies on the XPathNavigator class and other helperclasses such as XPathExpression and XPathNodeIterator.   Once the SortByDate method is called, the nodeparameter's MoveNext method is called to move to the first node in thenode set. This is necessary to get the correct context node being processed bythe XSLT processor. The nodes object's Current property is then calledto access the underlying XPathNavigator object named nav. The navobject is needed to compile an XPath statement and return an XPathExpressionobject. It's also needed to execute an XPath statement and locate specificnodes in the source XML document.   Because the XPathExpression class' AddSortmethod doesn't natively support sorting by date, I created another class named DateComparerXSLTthat implements the IComparer interface (see Figure 4). This customobject is passed to the AddSort method so that dates can be properlysorted. IComparer contains a single method named Compare that'sused to do comparisons between objects. The integer value returned from Compareis used to properly sort data when the XPath statement is executed.   public classDateComparerXSLT : IComparer {  publicDateComparerXSLT() {}    public intCompare(object date1, object date2)   {    int intResult;     DateTime d1 =Convert.ToDateTime(date1);     DateTime d2 =Convert.ToDateTime(date2);     intResult =DateTime.Compare(d1,d2);     return intResult;   }}Figure 4: Performing custom comparisons betweenobjects is accomplished by implementing the IComparer interface. The Comparemethod returns -1, 0, or 1 depending on how the two objects being comparedmatch up.   After the sort is added to the XPathExpressionobject (named exp) shown earlier inFigure 3, this code is called to execute the XPath statement contained in exp and perform the sort:   // Select nodes so we can iteratethrough them, // and see the sort. return (XPathNodeIterator)nav.Select(exp);   Using the XSLT Extension Object To call the SortByDate method from within an XSLTStylesheet, the code controlling the XSLT transformation must pass theextension object to the stylesheet. To do this, an XslTransform objectmust be created along with an XsltArgumentList object. The XsltArgumentListhas a method named AddExtensionObject that can be used to associate anextension object with a stylesheet. The code in Figure 5 creates these objectsand performs the XSLT transformation.   string dateType = "LongDate"; StringWriter sw = new StringWriter();XPathDocument doc = new XPathDocument(  Server.MapPath("XML/Customers.xml")); XslTransform trans = new XslTransform();  // Load stylesheet and provide evidence so extension// object can be used. trans.Load(new XPathDocument(Server.MapPath(  "XSLT/Customers.xslt")).CreateNavigator(),  new XmlUrlResolver(),this.GetType().Assembly.Evidence);   // Create and add extension object and DateType param. // DateType param determines how dates are displayed. XSLTDateTime sorter = new XSLTDateTime();XsltArgumentList args = new XsltArgumentList();args.AddExtensionObject(  "urn:xsltExtension-XSLTDateTime", sorter); args.AddParam("DateType", "", dateType); trans.Transform(doc, args, sw, null); Figure 5: The XsltArgumentListclass can be used to add parameter data and extension objects to stylesheetsfor use in an XSLT transformation.   When AddExtensionObject is called, the namespaceURI (associated with the extension object namespace prefix defined in the XSLT stylesheet)must be passed along with the actual object instance. The stylesheet knows howto handle the extension object because of the inclusion of a custom namespaceURI. Notice that the URI string passed to AddExtensionObject in Figure 5matches the one defined by the XSLTDateTime namespace prefix shown here:   < xsl:stylesheetversion="1.0"    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"    xmlns:XSLTDateTime="urn:xsltExtension-XSLTDateTime"> ...   Once the extension object is passed to the stylesheet, theXSLTDateTime object's SortByDate method can be called from withinthe stylesheet to retrieve a node set containing Customer nodes that are sortedbased on date data found in the CustomerSince child node (see Figure 6).         "XSLTDateTime:SortByDate(.,'Customer','CustomerSince')">                                      "XSLTDateTime:ToDateString(CustomerSince,$DateType)"/>                Figure 6: Callingan extension object method from within an XSLT stylesheet is accomplished byprefixing the method name with a namespace prefix that's associated with theextension object.   The XSLTDateTime extension object contains anadditional method named ToDateString that can be called to convertnumeric dates to long and short formats (see Figure 7). Output is generated bycalling the SortByDate and ToDateString methods from within anXSLT stylesheet (see Figure 8).   public string ToDateString(XPathNodeIterator node,   string dateType) {  // Try to convertdateType to DateTypeEnum.   DateTypeEnum type;   try   {    type =(DateTypeEnum)Enum.Parse(             typeof(DateTypeEnum),dateType,true);   }  catch   {    return "InvalidDateTypeEnum type passed. " +           "You canuse 'LongDate' or 'ShortDate'";   }  node.MoveNext();  // Position nav on first node.   XPathNavigator nav =node.Current;   string val = GetNodeValue(nav);   switch (type)   {    caseDateTypeEnum.ShortDate:       try       {        returnDateTime.Parse(val).ToShortDateString();      }       catch       {        return val;       }    caseDateTypeEnum.LongDate:       try       {         returnDateTime.Parse(val).ToLongDateString();      }       catch       {        return val;       }    default:       return val;   }}Figure 7: The ToDateStringmethod converts a string value of "LongDate" or "ShortDate" to a customenumeration, and then converts the appropriate date data to the desired outputformat.  
Figure 8: The data shown here issorted based on date data found within a node named CustomerSince (see Figure1). Notice that in addition to properly sorting by date, the numeric datesfound in the XML document have been converted to friendlier string versions ofthe date.   Conclusion Certainly, XSLT extension objects aren't needed every timeXML data is transformed, but they can be quite useful in certain situations. Inthis article you've seen one potential way to leverage extension objects tosort XML data based on dates. You've also seen how code can be embeddeddirectly into XSLT stylesheet script blocks.   Looking to the future of XSLT, in version 2.0 you won't berequired to go to a lot of extra effort to sort dates while transforming XMLdocuments because sorting on specific data types defined in XML schemas will benatively supported (as of this writing XSLT 2.0 was a "Working Draft"). UntilXSLT 2.0 is released and supported by the .NET Framework, the XSLTDateTimeextension object provides a fairly simple way to handle date sorting.   The sample code in this article is available for download.To view a live example of the code, visit Dan's XML for ASP.NET Developers Website: http://www.xmlforasp.net/CodeSection.aspx?csID=104.   Dan Wahlin(Microsoft Most Valuable Professional for ASP.NET and XML Web services) is thepresident of Wahlin Consulting and founded the XML for ASP.NET Developers Website (http://www.XMLforASP.NET), whichfocuses on using XML and Web services in Microsoft's .NET platform. He's also acorporate trainer and speaker, and teaches XML and .NET training courses aroundthe United States. Dan coauthored ProfessionalWindows DNA (Wrox, 2000) and ASP.NET:Tips, Tutorials and Code (SAMS,2001), and authored XML for ASP.NET Developers (SAMS, 2001).      

Sign up for the ITPro Today newsletter
Stay on top of the IT universe with commentary, news analysis, how-to's, and tips delivered to your inbox daily.

You May Also Like