Grouping in XSLT 2.0
The meat of this XSLT application lies in the product template, which takes advantage of XSLT 2.0's new grouping feature:
<xsl:variable name="name" select="name"/>
"current-group()[name = $name]/cost"/>
Total Qty:<xsl:value-of select="$Qty"/>
Price Each: <xsl:value-of select="$cost"/>
Grand Total: $<xsl:value-of
select="format-number($Qty * $cost,
If you look at the first two variables, name and Qty, you'll see there's nothing special happening there. It's just good old fashioned XSLT 1.0 stuff. The next variable, cost, is a 2.0 variable, though, because it uses the new grouping element as well as a new comparative operator that takes some of the pain out of grouping problems.
In production environments you are sometimes handed some weird XML, and this is no exception. In this case, some gremlin decided to break out the <price>
elements separately, but still left them within the same document as the ordering information. This isn't really too different than what we might find in the real world, however, in that case one might hope that the pricing info would be in a completely different document. Nevertheless, the grouping dilemma remains essentially the same.
The task here is to total the number of sales for each product and multiply that by the price of the product to determine the final sales value for each product. Although there are several ways to approach the problem, the first sample transformation uses the xsl:for-each-group element. But stay awake here! You've already seen how to declare a variable and access its value within the same expression, and you'll see a similar example later that accomplishes precisely the same task as you're about to see using the for-each-group
statement, but in a simpler fashion.
To apply the Muenchian method to grouping problems you need to eliminate duplicates to create distinct nodes in your output. Generally, you do that by comparing a current node with a target node generated by a key and seeing if they're identical. If they're not, you execute a series of statements to develop your group.
If you examine Listing 5
, which accomplishes the same grouping as Listing 4
, you'll see that I created a key at the top of the stylesheet. Then I had to compare the current node with the target (note the bolded code):
<xsl:variable name="pName" select="name" />
count(. |key('costKey',$name)) = 1]">
name = $pName]/cost"/>
The code for grouping can get pretty gnarly, but now, with XSLT 2.0, it's pretty simple, thanks to the new xsl:for-each-group element. Here's a for-each-group
The statement does pretty much what it says; it groups the <price>
elements by their child name elements which makes it easy to associate the <price>
elements with the names of the products being sold and simplifying the process of comparing the product names with the price names later. You can access the resulting sequence using the current-group()
function. The sample code uses it to compare the <price>
names to the product elements using the product element's name child. Conceptually, this is very similar to using keys, but I think you'll find it more intuitive.
If you like what you see here, you might want to dig a little deeper. For more examples on grouping, check out http://www.w3.org/TR/xslt20/#d5e13310
From this point, it's just a matter of multiplying the results of the two variables.