Web Development

New XSLT 2.0 features every application developer should know

XSL Transformations (XSLT) Version 2.0 is now a recommendation of the World Wide Web Consortium. Edmond Woychowsky describes several of the new features of XSLT 2.0 that will fulfill an immediate need in his application development and yours.

This article is also available as a TechRepublic download, which contains the same code (Listings A-L) in a more manageable text file format.

Unless you've been devoting all of your time to watching the Firefly box set and the movie Serenity that you got over the holidays and comparing family members' opening their gifts to Reavers sacking a town, you might have heard that XSLT 2.0 is now a recommendation of the World Wide Web Consortium. The question that comes to mind is--what does it mean that XSLT 2.0 is now a recommendation? Simply, it means that XSLT 2.0 is figuratively etched in stone and that those people who write XSLT processors can now run with it and write their XSLT processors without fear of being blindsided by eleventh-hour changes.

To me, however, the official adoption of XSLT 2.0 in conjunction with the holidays inspired me to create a short list of my favorite things about it. You can think of it as a "best of" collection of features that I think I'll be using most often. There were some features in XSLT 1.0 that I found myself using only about once or twice a year. Rather than concentrate on features that might not be of benefit now, I'll concentrate on the ones that will fulfill an immediate need in application development.

Grouping

It seems that whenever I find myself starting to write an XSL style sheet, the need to group information isn't far behind. This might be due to some deep-seated need to classify data, and thus make it more readable, or it just might be that I'm neurotic. Although it is just as likely that the person writing the requirements used to work for the state game commission: "You are allowed one trout and six pounds of game fish or one pound of game fish and one left-handed trout." If you've ever read the laws pertaining to fishing you get the idea.

With XSLT 1.0, grouping was something of an adventure, one of those things that required constant practice, like not being eaten by a Grue when spelunking. The preferred grouping method in XSLT 1.0 was something called Meunchian Grouping, which was developed by Steve Meunch of Oracle. The way that it worked was that an xsl:key element was coded, specifying the element or attribute that was to be used as the key as well as the element for which the key addressed. The grouping was accomplished by comparing a unique ID of the elements addressed by the first instance of each key with the same element in the document itself; when the IDs matched, the nodes were the same. This ensured that each key was processed only a single time at this point in the style sheet and that it was then possible to work with all elements having that key value.

A sample XML document and an example of Muenchian Grouping in XSLT 1.0 is shown in Listings A and B.

Listing A -- Sample XML document


<?xml version="1.0" encoding="UTF-8"?>
<world>
<country name="Canada" continent="North America">
<city>Toronto</city>
<city>Vancouver</city>
</country>
<country name="Jamaica" continent="North America">
<city>Kingston</city>
<city>Ocho Rios</city>
</country>
<country name="United States" continent="North America">
<city>Allentown</city>
<city>Mobile</city>
</country>
<country name="United Kingdom" continent="Europe">
<city>London</city>
<city>Dundee</city>
</country>
<country name="France" continent="Europe">
<city>Paris</city>
<city>Nice</city>
</country>
<country name="Japan" continent="Asia">
<city>Tokyo</city>
<city>Osaka</city>
</country>
</world>

Listing B -- Muenchian grouping in XSLT 1.0


<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format">
<xsl:key name="keyContinent" match="//country" use="@continent"/>
<xsl:template match="/">
<xsl:element name="world">
<xsl:for-each select="//country[generate-id(.) = generate-id(key('keyContinent',@continent)[1])]">
<xsl:sort select="@continent" data-type="text" order="ascending"/>
<xsl:variable name="continent" select="@continent"/>
<xsl:apply-templates select="//country[@continent = $continent]" mode="group">
<xsl:sort select="@name" data-type="text" order="ascending"/>
</xsl:apply-templates>
</xsl:for-each>
</xsl:element>
</xsl:template>
<xsl:template match="*" mode="group">
<xsl:copy-of select="."/>
</xsl:template>
</xsl:stylesheet>

With XSLT 2.0, things have changed on the grouping front somewhat, although I should point out that Meunchian Grouping will still work. The difference is that it is optional because support for grouping is now built in, so it really isn't necessary to use Muenchian Grouping anymore. Instead, developers can use an element like xsl:for-each-group, as demonstrated in Listing C.

Listing C -- Grouping in XSLT 2.0


<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions" xmlns:xdt="http://www.w3.org/2005/xpath-datatypes">
<xsl:template match="/">
<xsl:element name="world">
<xsl:for-each-group select="//country" group-by="@continent">
<xsl:sort select="@continent" data-type="text" order="ascending"/>
<xsl:variable name="continent" select="@continent"/>
<xsl:apply-templates select="//country[@continent = $continent]" mode="group">
<xsl:sort select="@name" data-type="text" order="ascending"/>
</xsl:apply-templates>
</xsl:for-each-group>
</xsl:element>
</xsl:template>
<xsl:template match="*" mode="group">
<xsl:copy-of select="."/>
</xsl:template>
</xsl:stylesheet>

As different as these methods of grouping are, the end result is the same, as Listing D shows.

Listing D -- The grouping result


<?xml version="1.0" encoding="UTF-8"?>
<world>
<country name="Japan" continent="Asia">
<city>Tokyo</city>
<city>Osaka</city>
</country>
<country name="France" continent="Europe">
<city>Paris</city>
<city>Nice</city>
</country>
<country name="United Kingdom" continent="Europe">
<city>London</city>
<city>Dundee</city>
</country>
<country name="Canada" continent="North America">
<city>Toronto</city>
<city>Vancouver</city>
</country>
<country name="Jamaica" continent="North America">
<city>Kingston</city>
<city>Ocho Rios</city>
</country>
<country name="United States" continent="North America">
<city>Allentown</city>
<city>Mobile</city>
</country>
</world>

Multiple result documents

One of the issues I always had with XSLT 1.0 was the limitations with style sheet output. I'm referring to the one document out kind of thing, where there was only one output document permitted. This meant that it was often necessary to play games once the result document was produced.

Take, for example, the XML document from Listing A and suppose that the assignment was to break it into multiple XML documents, one per country. In the past it would be necessary to write some code in the language of choice to create the individual XML documents. In XSLT 2.0 that has changed.

In XSLT 2.0, there is now the xsl:result-document element, which is used in conjunction with the xsl:output element to produce multiple result documents. The really interesting thing about this is that different xsl:output documents can be used to produce different document types through the use of the method attribute. The style sheet in Listing E shows one possible way to use this feature, and the XML documents in Listings F through L show the results.

Listing E -- Producing multiple result documents in XSLT 2.0


<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions" xmlns:xdt="http://www.w3.org/2005/xpath-datatypes">
<xsl:output name="xml" method="xml"/>
<xsl:template match="/">
<xsl:apply-templates select="//country"/>
</xsl:template>
<xsl:template match="country">
<xsl:variable name="name" select="concat(@name,'.xml')"/>
<xsl:result-document href="{$name}" format="xml">
<xsl:copy-of select="."/>
</xsl:result-document>
</xsl:template>
</xsl:stylesheet>

Listing F -- Canada.xml


<?xml version="1.0" encoding="UTF-8"?>
<country name="Canada" continent="North America">
<city>Toronto</city>
<city>Vancouver</city>
</country>

Listing H -- Jamiaca.xml


<?xml version="1.0" encoding="UTF-8"?>
<country name="Jamaica" continent="North America">
<city>Kingston</city>
<city>Ocho Rios</city>
</country>

Listing I -- United States.xml


<?xml version="1.0" encoding="UTF-8"?>
<country name="United States" continent="North America">
<city>Allentown</city>
<city>Mobile</city>
</country>

Listing J -- United Kingdom.xml


<?xml version="1.0" encoding="UTF-8"?>
<country name="United Kingdom" continent="Europe">
<city>London</city>
<city>Dundee</city>
</country>

Listing K -- France.xml


<?xml version="1.0" encoding="UTF-8"?>
<country name="France" continent="Europe">
<city>Paris</city>
<city>Nice</city>
</country>

Listing L -- Japan.xml


<?xml version="1.0" encoding="UTF-8"?>
<country name="Japan" continent="Asia">
<city>Tokyo</city>
<city>Osaka</city>
</country>

Other features

In addition to the features already listed, a couple of others make my favorite things list. The first of these is that XPath 2.0 is supported. As something of an XML geek, I look forward to trying out the new features and functions that XPath 2.0 offers. The second feature is one that really falls more in line with making XSLT more accessible to others, XQuery 1.0. Hopefully, XQuery 1.0 will open up XSLT 2.0 to those people who had allergic reactions to XPath.

XSLT 2.0 apps

Unfortunately, if you're interested in trying out the features of XSLT version 2.0, only a handful of processors are currently available. The first is Michael Kay's Saxon processor, an open source XSLT processor that has a tendency to be way ahead of everyone else when it comes to compliance with the World Wide Web Consortium's recommendations. A second possibility is Altova's XMLSpy, which is easily the fastest XML Editor I've ever seen. A free 30-day trial is available on their Web site; the only issue is that when the 30 days are up, you're going to want to buy it.

0 comments