'XSLT sort parents by child value changes element arrangements
I have the following simplified XML:
<Data>
<DataValue>Data</DataValue>
<Product>
<Price>60</Price>
<Value>Product Value</Value>
</Product>
<Product>
<Price>50</Price>
<Value>Product Value</Value>
</Product>
<Information>
<Entry>Some Information Text</Entry>
</Information>
<Description>
<Entry>Some Description Text</Entry>
</Description>
</Data>
I sort the product elements by the price value, with the following xslt code:
<xsl:template match="@*">
<xsl:attribute name="{name()}"><xsl:value-of select="normalize-space(.)"/></xsl:attribute>
</xsl:template>
<xsl:template match="*">
<xsl:element name="{local-name()}">
<xsl:apply-templates select="@*|node()"/>
</xsl:element>
</xsl:template>
<xsl:template match="*[local-name()='Data']">
<xsl:element name="{local-name()}">
<xsl:apply-templates select="@*"/>
<xsl:apply-templates select="*[not(local-name()='Product')]"/>
<xsl:apply-templates select="*[local-name()='Product']" >
<xsl:sort data-type="number" select="*[local-name()='Price']/text()" order="ascending"/>
</xsl:apply-templates>
</xsl:element>
</xsl:template>
Which leads to the following wrong result:
<Data>
<DataValue>Data</DataValue>
<Information>
<Entry>Some Information Text</Entry>
</Information>
<Description>
<Entry>Some Description Text</Entry>
</Description>
<Product>
<Price>50</Price>
<Value>Product Value</Value>
</Product>
<Product>
<Price>60</Price>
<Value>Product Value</Value>
</Product>
</Data>
The problem now is that the correctly sorted "Products" elements are placed at the end after the transformation. Is there a way to sort only parts of an xml while the other elements remain untouched and also remain on the same places?
Solution 1:[1]
Why can't you do simply:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/Data">
<xsl:copy>
<xsl:copy-of select="DataValue"/>
<xsl:for-each select="Product">
<xsl:sort select="Price" data-type="number"/>
<xsl:copy-of select="."/>
</xsl:for-each>
<xsl:copy-of select="Information | Description"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
P.S. There should never be a need to use a hack like *[local-name()='xyz']. If your input uses namespaces, deal with them properly - see here how: https://stackoverflow.com/a/34762628/3016153
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | michael.hor257k |
