I have to sort an xml file based on several values. The input xml has the following structure:
<DataSet>
<Product>
<Category>100</Category>
<ProductGroup>2000</ProductGroup>
<ProductNr>30003</ProductNr>
</Product>
<Product>
<Category>100</Category>
<ProductGroup>2000</ProductGroup>
<ProductNr>30001</ProductNr>
</Product>
<Product>
<Category>100</Category>
<ProductGroup>1000</ProductGroup>
<ProductNr>30002</ProductNr>
</Product>
<Product>
<Category>100</Category>
<ProductGroup>1000</ProductGroup>
<ProductNr>30004</ProductNr>
</Product>
<Product>
<Category>100</Category>
<ProductGroup>1000</ProductGroup>
<ProductNr>30010</ProductNr>
</Product>
<Product>
<Category>200</Category>
<ProductGroup>3000</ProductGroup>
<ProductNr>30006</ProductNr>
</Product>
<Product>
<Category>200</Category>
<ProductGroup>4000</ProductGroup>
<ProductNr>30009</ProductNr>
</Product>
<Product>
<Category>200</Category>
<ProductGroup>4000</ProductGroup>
<ProductNr>30007</ProductNr>
</Product>
<Product>
<Category>200</Category>
<ProductGroup>3000</ProductGroup>
<ProductNr>30005</ProductNr>
</Product>
This xml should be sorted by Category, then by ProductNr but grouped by ProductGroup. That means, if in ProductGroup 2000 there is a Product with a smaller ProductNr than in ProductGroup 1000, then ProductGroup 2000 should be printed befor ProductGroup 1000.
The output xml should look like this:
<DataSet>
<Product>
<Category>100</Category>
<ProductGroup>2000</ProductGroup>
<ProductNr>30001</ProductNr>
</Product>
<Product>
<Category>100</Category>
<ProductGroup>2000</ProductGroup>
<ProductNr>30003</ProductNr>
</Product>
<Product>
<Category>100</Category>
<ProductGroup>1000</ProductGroup>
<ProductNr>30002</ProductNr>
</Product>
<Product>
<Category>100</Category>
<ProductGroup>1000</ProductGroup>
<ProductNr>30004</ProductNr>
</Product>
<Product>
<Category>100</Category>
<ProductGroup>1000</ProductGroup>
<ProductNr>30010</ProductNr>
</Product>
<Product>
<Category>200</Category>
<ProductGroup>3000</ProductGroup>
<ProductNr>30005</ProductNr>
</Product>
<Product>
<Category>200</Category>
<ProductGroup>3000</ProductGroup>
<ProductNr>30006</ProductNr>
</Product>
<Product>
<Category>200</Category>
<ProductGroup>4000</ProductGroup>
<ProductNr>30007</ProductNr>
</Product>
<Product>
<Category>200</Category>
<ProductGroup>4000</ProductGroup>
<ProductNr>30009</ProductNr>
</Product>
Now my idea ist to generate an key for sorting purpose based on the following logic:
key = Category + (smallest ProductNr in ProductGroup) + ProductNr
Then I can sort by this key and have the right result.
I'm new to XSLT and don't know how to calculate the smallest ProductNr in ProductGroup and then sort by this value. Can you give me a hint?
Or: Is there a better technology to solve this task?
This should do it:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:key name="kProdInGroup" match="ProductNr" use="../ProductGroup" />
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/*">
<xsl:copy>
<xsl:apply-templates select="Product">
<!-- First sort by Category -->
<xsl:sort select="Category" data-type="number" />
<!-- Then by the min ProductNr per group -->
<xsl:sort select="key('kProdInGroup', ProductGroup)
[not(. > key('kProdInGroup', ../ProductGroup))]"
data-type="number" />
<!-- Then by ProductNr -->
<xsl:sort select="ProductNr" data-type="number" />
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
When applied to your sample input, the result is:
<DataSet>
<Product>
<Category>100</Category>
<ProductGroup>2000</ProductGroup>
<ProductNr>30001</ProductNr>
</Product>
<Product>
<Category>100</Category>
<ProductGroup>2000</ProductGroup>
<ProductNr>30003</ProductNr>
</Product>
<Product>
<Category>100</Category>
<ProductGroup>1000</ProductGroup>
<ProductNr>30002</ProductNr>
</Product>
<Product>
<Category>100</Category>
<ProductGroup>1000</ProductGroup>
<ProductNr>30004</ProductNr>
</Product>
<Product>
<Category>100</Category>
<ProductGroup>1000</ProductGroup>
<ProductNr>30010</ProductNr>
</Product>
<Product>
<Category>200</Category>
<ProductGroup>3000</ProductGroup>
<ProductNr>30005</ProductNr>
</Product>
<Product>
<Category>200</Category>
<ProductGroup>3000</ProductGroup>
<ProductNr>30006</ProductNr>
</Product>
<Product>
<Category>200</Category>
<ProductGroup>4000</ProductGroup>
<ProductNr>30007</ProductNr>
</Product>
<Product>
<Category>200</Category>
<ProductGroup>4000</ProductGroup>
<ProductNr>30009</ProductNr>
</Product>
</DataSet>