I\'m trying to find a solution to the following problem.
I\'m developing XSLT transformation (which is now about 40KB big) that is transforming quite complex XMLs i
Is it possible to apply xsl:sort on the output XML?
Yes, multipass processing is possible, and especially in XSLT 2.0 you don't even need to apply an xxx:node-set()
extension on the result, because the infamous RTF type does no longer exist:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<xsl:variable name="vPass1">
<!--
Put/Invoke your cirrent code here
to generate the following
-->
<Records>
<Record key="XX3">
</Record>
<Record key="XX2">
</Record>
<Record key="XX4">
</Record>
<Record key="XX1">
</Record>
</Records>
</xsl:variable>
<xsl:apply-templates select="$vPass1/*"/>
</xsl:template>
<xsl:template match="Records">
<Records>
<xsl:perform-sort select="*">
<xsl:sort select="@key"/>
</xsl:perform-sort>
</Records>
</xsl:template>
</xsl:stylesheet>
When this transformation is performed on any XML document (not used/ignored), the wanted, correct, sorted result is produced:
<Records>
<Record key="XX1"/>
<Record key="XX2"/>
<Record key="XX3"/>
<Record key="XX4"/>
</Records>
In XSLT 1.0 it is almost the same with the additional conversion of the result from RTF type to a normal tree:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ext="http://exslt.org/common"
exclude-result-prefixes="ext">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<xsl:variable name="vrtfPass1">
<!--
Put/Invoke your cirrent code here
to generate the following
-->
<Records>
<Record key="XX3">
</Record>
<Record key="XX2">
</Record>
<Record key="XX4">
</Record>
<Record key="XX1">
</Record>
</Records>
</xsl:variable>
<xsl:variable name="vPass1"
select="ext:node-set($vrtfPass1)"/>
<xsl:apply-templates select="$vPass1/*"/>
</xsl:template>
<xsl:template match="Records">
<Records>
<xsl:for-each select="*">
<xsl:sort select="@key"/>
<xsl:copy-of select="."/>
</xsl:for-each>
</Records>
</xsl:template>
</xsl:stylesheet>
As an addendum to Dimitre's excellent solution above, if you're using an XSLT 1.0 processor (for example, .NET), the following can give you a pointer as to how to use node-set: http://www.xml.com/pub/a/2003/07/16/nodeset.html#tab.namespaces
In my case, I was in .NET 1.1 (i.e. MSXML) and the solution looked something like:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt">
<xsl:template match="/">
<xsl:variable name="vrtfPass1">
<Records xmlns="">
<xsl:apply-templates />
</Records >
</xsl:variable>
<xsl:variable name="vPass1" select="msxsl:node-set($vrtfPass1)"/>
<xsl:apply-templates select="$vPass1/*" mode="sorting"/>
</xsl:template>
<xsl:template match="Records" mode="sorting">
<Records>
<xsl:for-each select="Record">
<xsl:sort select="@key"/>
<xsl:copy-of select="."/>
</xsl:for-each>
</Records>
</xsl:template>
</xsl:stylesheet>
40Kb is a lot of code for one stylesheet. When things get to this kind of scale, it's usually best to split a transformation into a pipeline of smaller transformations. If you have such a pipeline architecture, then adding a sort step at the end is trivial. There are plenty of technologies for managing a pipeline of transformations (XProc, Orbeon, xmlsh, ant, Coccoon) depending on your requirements. The benefit of pipelining is that it keeps your code modular and reusable.