5

Is there a way I can sort an xml file base on certain attributes with Groovy?

Here's my xml

<List>
    <Person name="fff"/>
    <Person name="ggg">
        <PhoneNum>
            <AreaCode>555</AreaCode>
            <Number>1234567</Number>
        </PhoneNum>
    </Person>
    <Person name="eee"/>
    <Person name="ccc"/>
    <Person name="jjj"/>
    <Person name="ddd">
        <PhoneNum>
            <AreaCode>555</AreaCode>
            <Number>7654321</Number>
        </PhoneNum>
    </Person>
    <Person name="aaa"/>
    <Person name="bbb"/>
    <Person name="ttt"/>
</List>

and I want the output to be

<List>
    <Person name="aaa"/>
    <Person name="bbb"/>
    <Person name="ccc"/>
    <Person name="ddd">
        <PhoneNum>
            <AreaCode>555</AreaCode>
            <Number>7654321</Number>
        </PhoneNum>
    </Person>
    <Person name="eee"/>
    <Person name="fff"/>
    <Person name="ggg">
        <PhoneNum>
            <AreaCode>555</AreaCode>
            <Number>1234567</Number>
        </PhoneNum>
    </Person>
    <Person name="jjj"/>
    <Person name="ttt"/>
</List>

I've looked into XMLSlurper but I can't quite seem to figure out how to do this.

Ryan Stull
  • 1,056
  • 14
  • 35

2 Answers2

7

Here's a modification to @dmahapatro's answer that preserves the nested node structure.

import groovy.xml.MarkupBuilder

String xml = '''
<List>
    <Person name="fff"/>
    <Person name="ggg">
        <PhoneNum>
            <AreaCode>555</AreaCode>
            <Number>1234567</Number>
        </PhoneNum>
    </Person>
    <Person name="eee"/>
    <Person name="ccc"/>
    <Person name="jjj"/>
    <Person name="ddd">
        <PhoneNum>
            <AreaCode>555</AreaCode>
            <Number>7654321</Number>
        </PhoneNum>
    </Person>
    <Person name="aaa"/>
    <Person name="bbb"/>
    <Person name="ttt"/>
</List>
'''

def rootNode = new XmlParser().parseText(xml)
rootNode.children().sort(true) {it.attribute('name')}
new XmlNodePrinter().print(rootNode)

Here's what's going on:

  • Using XmlParser instead of XmlSlurper generates nodes that can be printed using XmlNodePrinter.
  • The children of the node are sorted by name using sort {it.attribute('name')}
  • The true attribute to sort mutates the underlying list, which reorders the child nodes.
  • The XmlNodePrinter prints the re-sorted xml document to standard out.
Sean Reilly
  • 21,526
  • 4
  • 48
  • 62
1

I think there can be a groovier way than this. But this should work on a Friday. :-)

import groovy.xml.MarkupBuilder

def xml = '''<List>
    <Person name="fff"/>
    <Person name="eee"/>
    <Person name="ccc"/>
    <Person name="jjj"/>
    <Person name="aaa"/>
    <Person name="bbb"/>
    <Person name="ttt"/>
</List>'''

def rootNode = new XmlSlurper().parseText(xml)

def writer = new StringWriter()
def mkp = new MarkupBuilder(writer)
mkp.List{
    rootNode.Person.@name.list()*.toString().sort().each{
        Person(name: it)
    }
}

println writer
dmahapatro
  • 49,365
  • 7
  • 88
  • 117
  • Is there a way to make that work while retaining child elements of the elements that are to be sorted? I'll add an edit to my question so you can see – Ryan Stull Aug 09 '13 at 17:59