Reputation: 3
I am trying to come up with an XSLT that will sort the elements in the XML below while preserving the position of the other elements.
<TransactionSet TransactionSet_Id="12345">
<ATransaction ATransaction_Id="54321">
<DoNotSort_1 DoNotSort_1_Id="90678">
<SomeData/>
</DoNotSort_1>
<DoNotSort_2 DoNotSort_2_Id="46456">
<OtherData/>
</DoNotSort_2>
<DoNotSort_3 DoNotSort_3_Id="33333"/>
<SortIt SortIt_Id="11">
<TheOrder>1</TheOrder>
<MoreData/>
</SortIt>
<SortIt SortIt_Id="55">
<TheOrder>5</TheOrder>
<MoreData/>
</SortIt>
<SortIt SortIt_Id="22">
<TheOrder>2</TheOrder>
<MoreData/>
</SortIt>
<SortIt SortIt_Id="44">
<TheOrder>4</TheOrder>
<MoreData/>
</SortIt>
<DoNotSort_4 DoNotSort_4_Id="789456">
<EvenMoreData/>
</DoNotSort_4>
<DoNotSort_4 DoNotSort_4_Id="899567">
<EvenMoreData/>
</DoNotSort_4>
<DoNotSort_5 DoNotSort_5_Id="55555">
<EvenMoreData/>
</DoNotSort_5>
</ATransaction>
The XSLT I applied is below:
<xsl:stylesheet
version="1.0"
xmlns="http://www.oneshield.com/DragonSchema"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt">
<xsl:strip-space elements="*" />
<xsl:output method="xml" indent="yes"/>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="ATransaction">
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:apply-templates>
<xsl:sort select= "TheOrder" data-type="number"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
The output XML I am getting is below:
<TransactionSet TransactionSet_Id="12345">
<ATransaction ATransaction_Id="54321">
<DoNotSort_1 DoNotSort_1_Id="90678">
<SomeData/>
</DoNotSort_1>
<DoNotSort_2 DoNotSort_2_Id="46456">
<OtherData/>
</DoNotSort_2>
<DoNotSort_3 DoNotSort_3_Id="33333"/>
<DoNotSort_4 DoNotSort_4_Id="789456">
<EvenMoreData/>
</DoNotSort_4>
<DoNotSort_4 DoNotSort_4_Id="899567">
<EvenMoreData/>
</DoNotSort_4>
<DoNotSort_5 DoNotSort_5_Id="55555">
<EvenMoreData/>
</DoNotSort_5>
<SortIt SortIt_Id="11">
<TheOrder>1</TheOrder>
<MoreData/>
</SortIt>
<SortIt SortIt_Id="22">
<TheOrder>2</TheOrder>
<MoreData/>
</SortIt>
<SortIt SortIt_Id="44">
<TheOrder>4</TheOrder>
<MoreData/>
</SortIt>
<SortIt SortIt_Id="55">
<TheOrder>5</TheOrder>
<MoreData/>
</SortIt>
</ATransaction>
</TransactionSet>
My issue is: even though the elements are sorted, the and elements have been moved above the elements. I want them to be in their proper position, under the elements.
Any help with this will be greatly appreciated. Thanks in advance.
Upvotes: 0
Views: 54
Reputation: 117043
If the nodes you want to be sorted come always in a single contiguous block, you could 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:strip-space elements="*"/>
<!-- identity transform -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="ATransaction">
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:apply-templates select="SortIt[1]/preceding-sibling::*"/>
<xsl:apply-templates select="SortIt">
<xsl:sort select= "TheOrder" data-type="number"/>
</xsl:apply-templates>
<xsl:apply-templates select="SortIt[last()]/following-sibling::*"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Upvotes: 1
Reputation: 167651
In XSLT 2 or 3, assuming you would want to sort adjacent SortIt
elements, you could use for-each-group group-adjacent="boolean(self::SortIt)"
. In XSLT 1, you can try to use a key to identify adjacent SortIt
and then process and sort them together:
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output method="xml" indent="yes" />
<xsl:strip-space elements="*"/>
<xsl:key name="adjacent" match="SortIt[preceding-sibling::*[1][self::SortIt]]" use="generate-id(preceding-sibling::SortIt[not(preceding-sibling::*[1][self::SortIt])][1])"/>
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="SortIt[preceding-sibling::*[1][self::SortIt]]"/>
<xsl:template match="SortIt[not(preceding-sibling::*[1][self::SortIt])]">
<xsl:for-each select=". | key('adjacent', generate-id())">
<xsl:sort select="TheOrder" data-type="number"/>
<xsl:copy-of select="."/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/a9HjZm
Upvotes: 0