Here is the XSLT 2.0 style sheet that should do the following:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"> <xsl:output indent="yes"/> <xsl:template match="/"> <step> <Products> <xsl:for-each-group select="document(index/file)/step/Products/Product" group-by="Name"> <Product UserTypeID="{@UserTypeID}"> <Name><xsl:value-of select="current-grouping-key()"/></Name> <xsl:for-each-group select="current-group()/Product" group-by="Name"> <xsl:sort select="current-grouping-key()"/> <Product UserTypeID="{@UserTypeID}"> <Name><xsl:value-of select="current-grouping-key()"/></Name> <xsl:for-each select="current-group()/Product"> <xsl:sort select="Name"/> <xsl:copy-of select="."/> </xsl:for-each> </Product> </xsl:for-each-group> </Product> </xsl:for-each-group> </Products> </step> </xsl:template> </xsl:stylesheet>
You need to run it against an index XML document with a structure
<index> <file>test2010020803.xml</file> <file>test2010020804.xml</file> <file>test2010020805.xml</file> </index>
which lists the other files you want to process.
XSLT 2.0 style sheets can be run with Saxon 9 , which comes in .NET and the Java version, so it works wherever at least Java 1.5 or .NET 2.0 is available or can be installed. Other options are AltovaXML Tools (Windows only) and Gestalt .
If you are attached to XSLT 1.0, you can do it as follows if you have exsl: node -set or similar support:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exsl="http://exslt.org/common" exclude-result-prefixes="exsl" version="1.0"> <xsl:output indent="yes"/> <xsl:strip-space elements="*"/> <xsl:key name="k1" match="step/Products/Product" use="Name"/> <xsl:key name="k2" match="step/Products/Product/Product" use="concat(../Name, '|', Name)"/> <xsl:template match="/"> <xsl:variable name="rtf"> <xsl:copy-of select="document(index/file)/*"/> </xsl:variable> <step> <Products> <xsl:for-each select="exsl:node-set($rtf)/step/Products/Product[generate-id() = generate-id(key('k1', Name)[1])]"> <Product UserTypeID="{@UserTypeID}"> <xsl:copy-of select="Name"/> <xsl:for-each select="key('k1', Name)/Product[generate-id() = generate-id(key('k2', concat(../Name, '|', Name))[1])]"> <xsl:sort select="Name"/> <Product UserTypeID="{@UserTypeID}"> <xsl:copy-of select="Name"/> <xsl:for-each select="key('k2', concat(../Name, '|', Name))/Product"> <xsl:sort select="Name"/> <xsl:copy-of select="."/> </xsl:for-each> </Product> </xsl:for-each> </Product> </xsl:for-each> </Products> </step> </xsl:template> </xsl:stylesheet>
The keys will look like this:
<xsl:key name="k1" match="step/Products/Product" use="Name"/> <xsl:key name="k2" match="step/Products/Product/Product" use="concat(../Name, '|', Name)"/> <xsl:key name="k3" match="step/Products/Product/Product/Product" use="concat(../../Name, '|', ../Name, '|', Name)"/> <xsl:key name="k4" match="step/Products/Product/Product/Product/Product" use="concat(../../../Name, '|', ../../Name, '|', ../Name, '|', Name)"/> <xsl:key name="k5" match="step/Products/Product/Product/Product/Product/Product" use="concat(../../../../Name, '|', ../../../Name, '|', ../../Name, '|', ../Name, '|', Name)"/> <xsl:key name="k6" match="step/Products/Product/Product/Product/Product/Product/Product" use="concat(../../../../../Name, '|', ../../../../Name, '|', ../../../Name, '|', ../../Name, '|', ../Name, '|', Name)"/>
This is all printed here directly in the forum editor, so there may be errors.