gamedev
gamedev

Reputation: 15

Sorting xml nodes in groovy

I have an XML, to which I need to add new nodes, which I have achieved using appendNode. However, the newly added node is put at the end of the XML. I would like to now sort this XML so that it goes to the correct position:

<Order>
<Customer>
...
</Customer>
<item>
    <itemID>1</itemID>
</item>
<item>
    <parentItemID>1</parentItemID>
    <priority>25</priority>
</item>
<item>
     <itemID>2</itemID>
</item>
<deliverydetails>
</deliverydetails>
<invoiceTerms>
....
</invoiceTerms>

//this is my newly added item
<item>
    <parentItemID>2</parentItemID>
    <priority>35</priority>
</item>
</Order>

I need to reorder it so that it appears at the top like:

<Order>
<Customer>
...
</Customer>
<item>
    <itemID>1</itemID>
</item>
<item>
    <parentItemID>1</parentItemID>
    <priority>25</priority>
</item>
<item>
     <itemID>2</itemID>
</item>
<item>
    <parentItemID>2</parentItemID>
    <priority>35</priority>
</item>
<deliverydetails>
</deliverydetails>
<invoiceTerms>
....
</invoiceTerms>
</Order>

Tried the following Code:

Node root = new XmlParser().parse(xml);
def orderNode = root.Order;
....
orderNode[0].children().sort(true) {it.item.parentItemID.text()}

Upvotes: 1

Views: 699

Answers (1)

michael.hor257k
michael.hor257k

Reputation: 116959

I only need to put that last item node that I newly added so that it appears together with the other item nodes, rather than at the end (preferably together with the item ID specified under parentItemID so that items and their related sub-items are together)

If you are sure that an item with an itemID of 2 (your newly-added item's parentItemID) will appear in the input XML, 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="item[itemID=2]">
    <xsl:copy-of select="."/>
    <item>
        <parentItemID>2</parentItemID>
        <priority>35</priority>
    </item>
</xsl:template>
        
</xsl:stylesheet>

-- added --

If you only want to reorder the nodes so that all item elements are kept together (after the new item has already been added), then you could do:

<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="*"/>

<xsl:template match="/Order">
    <xsl:copy>
        <xsl:copy-of select="Customer"/>
        <xsl:copy-of select="item"/>
        <xsl:copy-of select="deliverydetails"/>
        <xsl:copy-of select="invoiceTerms"/>
    </xsl:copy>
</xsl:template>

</xsl:stylesheet>

If you want to make sure that the newly added item comes after its parent item (or, more precisely, that every sub-item comes after its parent item), try:

<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="*"/>

<xsl:key name="sub-item" match="item" use="parentItemID" />

<xsl:template match="/Order">
    <xsl:copy>
        <xsl:copy-of select="Customer"/>
        <xsl:for-each select="item[not(parentItemID)]">
            <xsl:copy-of select="."/>
            <xsl:copy-of select="key('sub-item', itemID)"/>
        </xsl:for-each>
        <xsl:copy-of select="deliverydetails"/>
        <xsl:copy-of select="invoiceTerms"/>
    </xsl:copy>
</xsl:template>

</xsl:stylesheet>

Upvotes: 1

Related Questions