Loading...

Thursday, May 26, 2011

Groovy Goodness: Change XML Structure

We know how easy it is to read XML and to write XML. Recently someone asked during a workshop if it is possible to change the XML structure as easy. I found the answer on the Groovy website. It turns out we can simple use XmlSlurper and we can traverse the structure to change existing node values and attributes. We use appendNode() and replaceNode() to work with nodes using a builder syntax.

@Grapes(
    @Grab(group='xmlunit', module='xmlunit', version='1.3')
)
import org.custommonkey.xmlunit.*
import groovy.xml.*

def xml = '''
<todo>
    <item priority="2">
        <title>Look into GPars</title>
    </item>
    <item priority="1">
        <title>Start developing Griffon App</title>
    </item>
    <item priority="2">
        <title>Grails 1.4 M1</title>
    </item>
    <item priority="3">
        <title>GWT Sample</title>
    </item>
</todo>
'''

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

// Change node values.
def items = todo.item.findAll { 
   it.@priority.toInteger() < 3 
}
items.each { item -> 
    item.title = "DO: " + item.title 
}

// Change attribute value.
def gpars= todo.item.find { 
    it.title =~ /.*GPars.*/ 
}
gpars.@priority = '1'

// Add extra item node.
todo.appendNode {
    item(priority: 2) {
        title 'Work on blog post'
    }
}

// Change node.
def grailsItem = todo.item.find { 
    it.title.toString().contains('Grails') 
}
grailsItem.replaceNode { node ->
    item(who: 'mrhaki', priority: node.@priority) {
        title 'Download Grails 1.4 M1'       
    }
}

// Remove node. Index value based on result directly after parsing.
// So here we remove the item about GWT.
todo.item[3].replaceNode {}

// Create output.
def newTodo = new StreamingMarkupBuilder().bind { 
    mkp.yield todo 
}.toString()

def expected = '''
<todo>
    <item priority="1">
        <title>DO: Look into GPars</title>
    </item>
    <item priority="1">
        <title>DO: Start developing Griffon App</title>
    </item>
    <item who="mrhaki" priority="2">
        <title>Download Grails 1.4 M1</title>
    </item>
    <item priority="2">
        <title>Work on blog post</title>
    </item>
</todo>
'''

// Check to see expected XML equals new todo XML.
XMLUnit.ignoreWhitespace = true
def difference = new Diff(newTodo, expected)
assert difference.similar()

4 comments:

Andrey Paramonov said...

Small typo: should be 'new Diff(result, expected)' instead of 'new Diff(newTodo, expected)'

mrhaki said...

@Andrey Paramonov: thank you for finding the type. I've changed the sample code.

david said...

out of curiosity, what software do you use to edit your xml files? I am tried a few free products and have trialled liquid xml (http://www.liquid-technologies.com/xml-editor.aspx) also, do you have a preference?

mrhaki said...

@david: I haven't used any specific XML editor lately, but I really liked Oxygen (http://www.oxygenxml.com/).

Post a Comment