I found an SO post titled XSLT sort by attribute value whose objective is similar to mine. I am trying to sort xml nodes by attribute value using XSLT 1.0. But when I tried to apply the accepted solution, I got unexpected results.
Here is some very stripped-down xml from my system upon which I'm trying to achieve XSLT-based sorting.
<?xml version="1.0" encoding="UTF-8"?>
<mpcbom xmlns:bv="urn:BomViewXmlUtil" xmlns:msxsl="urn:schemas-microsoft-com:xslt" seriesid="Flatbed_A" seriesdesc="Flatbed A" modelid="FB2" modeldesc="Flatbed Aluminum 123" pricelistid="0">
<propDefn>
<prop seriesid="Flatbed_A" id="AddCharges" text="AddCharges" />
</propDefn>
<assembly seriesid="Flatbed_A" id="102B-1" name="Flatbed_B" qty="1" price="123" catid="Finish_unit">
<prop id="PriceOverride" val="False" />
<prop id="SuggestedPrice" val="54005" />
<prop id="TabSeqNum" val="0" src="Assembly" />
<operation seriesid="Flatbed_A" id="ALC-FOB" name="Client delivery" qty="1" price="0" catid="O_ABC123">
<prop id="HostOptionID" val="ALC-FOB" />
<prop id="ProcessTimeStd" val="0" />
<prop id="ProcessTimeXtra" val="0" />
</operation>
<assembly seriesid="Flatbed_A" id="102B-2" name="Unit_Offline" qty="1" price="0" catid="Unit_Offline">
<prop id="HostOptionID" val="" />
<prop id="ERP_RouteId" val="" />
<assembly seriesid="Flatbed_A" id="Step70" name="Step70" qty="1" price="0" catid="Step70">
<prop id="HostOptionID" val="Step70" />
<prop id="BOMUOM_From_BYOD" val="UN / EA" />
<operation seriesid="Flatbed_A" id="523-110" name="Washing" qty="1" price="0" catid="O_523_110">
<prop id="HostOptionID" val="523-110" />
<prop id="ProcessTimeStd" val="3.5" />
</operation>
<operation seriesid="Flatbed_A" id="523-120" name="Finish1" qty="1" price="0" catid="O_523_120">
<prop id="HostOptionID" val="523-120" />
<prop id="ProcessTimeStd" val="0.5" />
</operation>
<material seriesid="Flatbed_A" id="3UN5020000" name="Rubber1" qty="1" price="0" catid="Rear_Bumper_Option_Assy">
<prop id="Cost" val="124.9899768" />
<prop id="ExtCost" val="124.9899768" />
</material>
<material seriesid="Flatbed_A" id="3UN5990000" name="Round1" qty="1" price="0" catid="Doc_Holder_Assy">
<prop id="Cost" val="5.11" />
<prop id="ExtCost" val="5.11" />
</material>
</assembly>
<assembly seriesid="Flatbed_A" id="102B-3" name="ABC1" qty="1" price="0" catid="Assembled_Unit">
<prop id="CatID" val="Assembled_Unit" />
<prop id="HostOptionID" val="102B-3" />
<assembly seriesid="Flatbed_A" id="Step10" name="Step10" qty="1" price="0" catid="Step10">
<prop id="HostOptionID" val="Step10" />
<prop id="BOMUOM_From_BYOD" val="UN / EA" />
<operation seriesid="Flatbed_A" id="510-110" name="Assemblage 1" qty="1" price="0" catid="O_510_110">
<prop id="HostOptionID" val="510-110" />
<prop id="ProcessTimeStd" val="13.5" />
</operation>
<material seriesid="Flatbed_A" id="XYZ123" name="Front rail" qty="1" price="0" catid="Front_Rail_assy">
<prop id="Cost" val="80.679768" />
<prop id="ExtCost" val="80.679768" />
</material>
<material seriesid="Flatbed_A" id="FB2C53XXXA" name="53ft 2 axles Flatbed" qty="1" price="0" catid="Structure_Assy">
<prop id="Cost" val="12901.27129" />
<prop id="ExtCost" val="12901.27129" />
</material>
<material seriesid="Flatbed_A" id="ABC123" name="Front rail" qty="1" price="0" catid="Front_Rail_assy">
<prop id="Cost" val="80.679768" />
<prop id="ExtCost" val="80.679768" />
</material>
</assembly>
<assembly seriesid="Flatbed_A" id="Step20" name="Step20" qty="1" price="0" catid="Step20">
<prop id="HostOptionID" val="Step20" />
<prop id="BOMUOM_From_BYOD" val="UN / EA" />
<operation seriesid="Flatbed_A" id="510-120" name="Assemblage 2" qty="1" price="0" catid="O_510_120">
<prop id="HostOptionID" val="510-120" />
<prop id="ProcessTimeStd" val="14" />
</operation>
<material seriesid="Flatbed_A" id="DEF123" name="PLANCHER 53" qty="1" price="0" catid="FloorAlu_Assy">
<prop id="Cost" val="3629.53568" />
<prop id="ExtCost" val="3629.53568" />
</material>
<material seriesid="Flatbed_A" id="STU123" name="6-4in" qty="1" price="0" catid="Rear_Bumper_Assy">
<prop id="Cost" val="773.383414" />
<prop id="ExtCost" val="773.383414" />
</material>
<material seriesid="Flatbed_A" id="ABC123" name="6-4in" qty="1" price="0" catid="Rear_Bumper_Assy">
<prop id="Cost" val="773.383414" />
<prop id="ExtCost" val="773.383414" />
</material>
</assembly>
</assembly>
</assembly>
</assembly>
</mpcbom>
And here is my base XSLT that does exactly what I need done, minus the sorting:
<?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"
exclude-result-prefixes="msxsl">
<xsl:output method="xml" encoding="UTF-8" omit-xml-declaration="yes" indent="no" version="1.0" standalone="yes" />
<xsl:variable name="lowercase">abcdefghijklmnopqrstuvwxyz</xsl:variable>
<xsl:variable name="uppercase">ABCDEFGHIJKLMNOPQRSTUVWXYZ</xsl:variable>
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="propDefn" />
<xsl:template match="material|operation">
<xsl:copy>
<xsl:apply-templates select="@*" />
<xsl:apply-templates select="prop">
<xsl:sort select="translate(@id, $uppercase, $lowercase)" />
</xsl:apply-templates>
<xsl:apply-templates select="*[not(self::prop)]" />
</xsl:copy>
</xsl:template>
<xsl:template match="@rid" />
<xsl:template match="material/@name|material/@name|operation/@name" />
<xsl:template match="@id|@catid">
<xsl:attribute name="{local-name()}">
<xsl:value-of select="translate(., $uppercase, $lowercase)" />
</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
Here is the xml result of the transformation above:
<?xml version="1.0" encoding="UTF-8"?>
<mpcbom xmlns:bv="urn:BomViewXmlUtil" xmlns:msxsl="urn:schemas-microsoft-com:xslt" seriesid="Flatbed_A" seriesdesc="Flatbed A" modelid="FB2" modeldesc="Flatbed Aluminum 123" pricelistid="0">
<assembly seriesid="Flatbed_A" id="102b-1" name="Flatbed_B" qty="1" price="123" catid="finish_unit">
<prop id="priceoverride" val="False" />
<prop id="suggestedprice" val="54005" />
<prop id="tabseqnum" val="0" src="Assembly" />
<operation seriesid="Flatbed_A" id="alc-fob" qty="1" price="0" catid="o_abc123">
<prop id="hostoptionid" val="ALC-FOB" />
<prop id="processtimestd" val="0" />
<prop id="processtimextra" val="0" />
</operation>
<assembly seriesid="Flatbed_A" id="102b-2" name="Unit_Offline" qty="1" price="0" catid="unit_offline">
<prop id="hostoptionid" val="" />
<prop id="erp_routeid" val="" />
<assembly seriesid="Flatbed_A" id="step70" name="Step70" qty="1" price="0" catid="step70">
<prop id="hostoptionid" val="Step70" />
<prop id="bomuom_from_byod" val="UN / EA" />
<operation seriesid="Flatbed_A" id="523-110" qty="1" price="0" catid="o_523_110">
<prop id="hostoptionid" val="523-110" />
<prop id="processtimestd" val="3.5" />
</operation>
<operation seriesid="Flatbed_A" id="523-120" qty="1" price="0" catid="o_523_120">
<prop id="hostoptionid" val="523-120" />
<prop id="processtimestd" val="0.5" />
</operation>
<material seriesid="Flatbed_A" id="3un5020000" qty="1" price="0" catid="rear_bumper_option_assy">
<prop id="cost" val="124.9899768" />
<prop id="extcost" val="124.9899768" />
</material>
<material seriesid="Flatbed_A" id="3un5990000" qty="1" price="0" catid="doc_holder_assy">
<prop id="cost" val="5.11" />
<prop id="extcost" val="5.11" />
</material>
</assembly>
<assembly seriesid="Flatbed_A" id="102b-3" name="ABC1" qty="1" price="0" catid="assembled_unit">
<prop id="catid" val="Assembled_Unit" />
<prop id="hostoptionid" val="102B-3" />
<assembly seriesid="Flatbed_A" id="step10" name="Step10" qty="1" price="0" catid="step10">
<prop id="hostoptionid" val="Step10" />
<prop id="bomuom_from_byod" val="UN / EA" />
<operation seriesid="Flatbed_A" id="510-110" qty="1" price="0" catid="o_510_110">
<prop id="hostoptionid" val="510-110" />
<prop id="processtimestd" val="13.5" />
</operation>
<material seriesid="Flatbed_A" id="xyz123" qty="1" price="0" catid="front_rail_assy">
<prop id="cost" val="80.679768" />
<prop id="extcost" val="80.679768" />
</material>
<material seriesid="Flatbed_A" id="fb2c53xxxa" qty="1" price="0" catid="structure_assy">
<prop id="cost" val="12901.27129" />
<prop id="extcost" val="12901.27129" />
</material>
<material seriesid="Flatbed_A" id="abc123" qty="1" price="0" catid="front_rail_assy">
<prop id="cost" val="80.679768" />
<prop id="extcost" val="80.679768" />
</material>
</assembly>
<assembly seriesid="Flatbed_A" id="step20" name="Step20" qty="1" price="0" catid="step20">
<prop id="hostoptionid" val="Step20" />
<prop id="bomuom_from_byod" val="UN / EA" />
<operation seriesid="Flatbed_A" id="510-120" qty="1" price="0" catid="o_510_120">
<prop id="hostoptionid" val="510-120" />
<prop id="processtimestd" val="14" />
</operation>
<material seriesid="Flatbed_A" id="def123" qty="1" price="0" catid="flooralu_assy">
<prop id="cost" val="3629.53568" />
<prop id="extcost" val="3629.53568" />
</material>
<material seriesid="Flatbed_A" id="stu123" qty="1" price="0" catid="rear_bumper_assy">
<prop id="cost" val="773.383414" />
<prop id="extcost" val="773.383414" />
</material>
<material seriesid="Flatbed_A" id="abc123" qty="1" price="0" catid="rear_bumper_assy">
<prop id="cost" val="773.383414" />
<prop id="extcost" val="773.383414" />
</material>
</assembly>
</assembly>
</assembly>
</assembly>
</mpcbom>
So far so good, but again, no sorting has been attempted yet. Now here is the same XSLT as above, but with my attempt at sorting added:
<?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"
exclude-result-prefixes="msxsl">
<xsl:output method="xml" encoding="UTF-8" omit-xml-declaration="yes" indent="no" version="1.0" standalone="yes" />
<xsl:variable name="lowercase">abcdefghijklmnopqrstuvwxyz</xsl:variable>
<xsl:variable name="uppercase">ABCDEFGHIJKLMNOPQRSTUVWXYZ</xsl:variable>
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="propDefn" />
<xsl:template match="material|operation">
<xsl:copy>
<xsl:apply-templates select="@*" />
<xsl:apply-templates select="prop">
<xsl:sort select="translate(@id, $uppercase, $lowercase)" />
</xsl:apply-templates>
<xsl:apply-templates select="*[not(self::prop)]" />
</xsl:copy>
</xsl:template>
<xsl:template match="assembly">
<xsl:copy>
<xsl:apply-templates select="assembly">
<xsl:sort select="material/@id"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:template match="@rid" />
<xsl:template match="material/@name|material/@name|operation/@name" />
<xsl:template match="@id|@catid">
<xsl:attribute name="{local-name()}">
<xsl:value-of select="translate(., $uppercase, $lowercase)" />
</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
For clarity, here is the section I added (trying to sort):
<xsl:template match="assembly">
<xsl:copy>
<xsl:apply-templates select="assembly">
<xsl:sort select="material/@id"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
And here is the disastrous result of the transformation (same xml source as above):
<?xml version="1.0" encoding="UTF-8"?>
<mpcbom xmlns:bv="urn:BomViewXmlUtil" xmlns:msxsl="urn:schemas-microsoft-com:xslt" seriesid="Flatbed_A" seriesdesc="Flatbed A" modelid="FB2" modeldesc="Flatbed Aluminum 123" pricelistid="0">
<assembly>
<assembly>
<assembly>
<assembly />
<assembly />
</assembly>
<assembly />
</assembly>
</assembly>
</mpcbom>
Clearly I'm a noob with XSLT...but here's what I'm trying to achieve: Whenever there are <material> nodes inside an <assembly> node, I want all the <material> nodes sorted by their id attributes in an alphanumeric fashion. For example, take this set of nodes from the source xml:
<material seriesid="Flatbed_A" id="XYZ123" name="Front rail" qty="1" price="0" catid="Front_Rail_assy">
<prop id="Cost" val="80.679768" />
<prop id="ExtCost" val="80.679768" />
</material>
<material seriesid="Flatbed_A" id="FB2C53XXXA" name="53ft 2 axles Flatbed" qty="1" price="0" catid="Structure_Assy">
<prop id="Cost" val="12901.27129" />
<prop id="ExtCost" val="12901.27129" />
</material>
<material seriesid="Flatbed_A" id="ABC123" name="Front rail" qty="1" price="0" catid="Front_Rail_assy">
<prop id="Cost" val="80.679768" />
<prop id="ExtCost" val="80.679768" />
</material>
Notice the ids of the 3 <material> nodes: XYZ123, FB2C53XXXA, ABC123. I need those sorted by their ids so that the transformed xml (just for that section) looks like this:
<material seriesid="Flatbed_A" id="ABC123" name="Front rail" qty="1" price="0" catid="Front_Rail_assy">
<prop id="Cost" val="80.679768" />
<prop id="ExtCost" val="80.679768" />
</material>
<material seriesid="Flatbed_A" id="FB2C53XXXA" name="53ft 2 axles Flatbed" qty="1" price="0" catid="Structure_Assy">
<prop id="Cost" val="12901.27129" />
<prop id="ExtCost" val="12901.27129" />
</material>
<material seriesid="Flatbed_A" id="XYZ123" name="Front rail" qty="1" price="0" catid="Front_Rail_assy">
<prop id="Cost" val="80.679768" />
<prop id="ExtCost" val="80.679768" />
</material>
Likewise for the 2nd set of <material> nodes. Original:
<material seriesid="Flatbed_A" id="DEF123" name="PLANCHER 53" qty="1" price="0" catid="FloorAlu_Assy">
<prop id="Cost" val="3629.53568" />
<prop id="ExtCost" val="3629.53568" />
</material>
<material seriesid="Flatbed_A" id="STU123" name="6-4in" qty="1" price="0" catid="Rear_Bumper_Assy">
<prop id="Cost" val="773.383414" />
<prop id="ExtCost" val="773.383414" />
</material>
<material seriesid="Flatbed_A" id="ABC123" name="6-4in" qty="1" price="0" catid="Rear_Bumper_Assy">
<prop id="Cost" val="773.383414" />
<prop id="ExtCost" val="773.383414" />
</material>
Desired sort, after transformation:
<material seriesid="Flatbed_A" id="ABC123" name="6-4in" qty="1" price="0" catid="Rear_Bumper_Assy">
<prop id="Cost" val="773.383414" />
<prop id="ExtCost" val="773.383414" />
</material>
<material seriesid="Flatbed_A" id="DEF123" name="PLANCHER 53" qty="1" price="0" catid="FloorAlu_Assy">
<prop id="Cost" val="3629.53568" />
<prop id="ExtCost" val="3629.53568" />
</material>
<material seriesid="Flatbed_A" id="STU123" name="6-4in" qty="1" price="0" catid="Rear_Bumper_Assy">
<prop id="Cost" val="773.383414" />
<prop id="ExtCost" val="773.383414" />
</material>
How is it done?