XSLT 2.0 in ColdFusion
While not totally on topic with this posting, but definitely worth reading is Steven Erat's explanation of using the XMLSearch function with default namespaces.
XSLT 2.0 was released as its W3C Candidate Recommendation 3 earlier this month (http://www.w3.org/TR/2005/CR-xslt20-20051103/). There are a lot of nice new features that go along with the new specification (user functions, grouping, multiple outputs, temporary trees, character mappings, datatype bindings, etc.). And, it's relatively painless to implement in ColdFusion.
The first thing you need is an XSLT 2.0 compliant parser. Right now, that means Saxon 8. You'll need to put at least the Saxon8.jar file into your Coldfusion classpath (see Christian Cantrell's cheat sheet).
The next part is to write a wrapper for the transformation. Because Mark Mandel already wrote a replacement for XMLTransform that allows for parameters, I used his xslt function code as a jumping off point. Really there's only one line that has to be changed (other than renaming the function). The code line
simply needs to be rewritten as
You can grab the function here.
So, now a quick example of the power of XSLT 2.0...
Let's say you have an XML file that lists cities in the US with their state and populations:
<cities>
<city name="Williamsburg" state="Virginia" pop="11998" />
<city name="New York City" state="New York" pop="80000" />
<city name="Washington" state="DC" pop="553523" />
<city name="Richmond" state="Virginia" pop="300000" />
</cities>
Now, you want to display cities grouped by state and output the state's total population.
In XSLT 1.0, you needed to rely on XPath queries with nested for loops:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:template match="/">
<table>
<xsl:for-each select="cities/city[not(@state=preceding::*/@state)]">
<tr>
<td><xsl:value-of select="@state" /></td>
<td>
<xsl:for-each select="../city[@state = current()/@state]">
<xsl:value-of select="@name"/>
<xsl:if test="position() != last()">, </xsl:if>
</xsl:for-each>
</td>
<td>
<xsl:value-of select="sum(../city[@state=current()/@state]/@pop)" />
</td>
</tr>
</xsl:for-each>
</table>
</xsl:template>
</xsl:stylesheet>
However, in XSLT 2.0, the for-each-group function makes this same transformation much more straight forward:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:template match="/">
<table>
<xsl:for-each-group select="cities/city" group-by="@state">
<tr>
<td><xsl:value-of select="@state" /></td>
<td>
<xsl:value-of select="current-group()/@name" separator=", " />
</td>
<td><xsl:value-of select="sum(current-group()/@pop)" /></td>
</tr>
</xsl:for-each-group>
</table>
</xsl:template>
</xsl:stylesheet>
Notice the difference in the amount of code, and how you have to access the different attributes. The function current-group() of the complexity of accessing the current node in your transformation, making development quicker and less buggy.
You can see the results from the above example here.
This seriously only scratches the surface of what you can do with ColdFusion and XSLT 2.0. The Saxon parser also implements the XQuery 1.0 and XPath 2.0 opening a whole new set of possibilities in ColdFusion.

Just wanted to let you know that your article on XSLT 2.0 in Cold Fusion helped me out a great deal. Even though you posted it a year and a half ago, the information is not the least bit stale. I had been using Xalan for so long (even in my Java development), that I completely forgot about Saxon, which I had initially liked quite a bit. Also, you link off to some really informative sites. Very well done article.
Thank you very much,
-Bif
Thanks! I was a bit disappointed that Adobe stayed with Xalan in CF8, but the Saxon engine works a lot faster, so at least there's that ;)
If things ease up a bit more, I've got some XQuery stuff sitting around to write up...