Working with XML
Prerequisites: Working with JSON
Groovy is famous for making XML pleasant. The groovy.xml package
parses XML into objects you navigate with GPath — dots for child
elements, @ for attributes.
Reading XML
import groovy.xml.XmlSlurper
def xml = '''
<library>
<book id="1" available="true">
<title>Dune</title><author>Frank Herbert</author><year>1965</year>
</book>
<book id="2" available="false">
<title>Neuromancer</title><author>William Gibson</author><year>1984</year>
</book>
<book id="3" available="true">
<title>Hyperion</title><author>Dan Simmons</author><year>1989</year>
</book>
</library>
'''
def library = new XmlSlurper().parseText(xml)
println library.book.size()
println library.book[0].title
println library.book*.title*.text()
println library.book.find { it.@id == '2' }.author
println library.book.findAll { it.@available == 'true' && it.year.toInteger() > 1980 }.title
println library.'**'.findAll { it.name() == 'year' }*.toInteger().sum()
3
Dune
[Dune, Neuromancer, Hyperion]
William Gibson
Hyperion
5938
library.bookis every<book>child;[0]picks one..@idreads an attribute;.text()gets an element's text.'**'searches the whole tree, depth-first.
Check or reformat XML with the XML Formatter and experiment with paths in the XPath Evaluator.
Writing XML with MarkupBuilder
import groovy.xml.MarkupBuilder
def writer = new StringWriter()
def xml = new MarkupBuilder(writer)
def items = [[sku: 'A1', qty: 2, price: 9.99], [sku: 'B7', qty: 1, price: 24.50]]
xml.order(id: 1001, status: 'paid') {
customer('Ada Lovelace')
items.each { item ->
line(sku: item.sku) {
quantity(item.qty)
price(currency: 'EUR', item.price)
}
}
}
println writer
<order id='1001' status='paid'>
<customer>Ada Lovelace</customer>
<line sku='A1'>
<quantity>2</quantity>
<price currency='EUR'>9.99</price>
</line>
<line sku='B7'>
<quantity>1</quantity>
<price currency='EUR'>24.50</price>
</line>
</order>
Each method call becomes an element, map arguments become attributes, and nested closures become children. Because it's just code, you can use loops and conditions inside.
Modifying XML
import groovy.xml.XmlParser
import groovy.xml.XmlNodePrinter
def root = new XmlParser().parseText('<config><db host="localhost" port="5432"/></config>')
root.db[0].@host = 'db.internal'
root.appendNode('cache', [ttl: '60'])
def out = new StringWriter()
new XmlNodePrinter(new PrintWriter(out)).print(root)
println out
<config>
<db host="db.internal" port="5432"/>
<cache ttl="60"/>
</config>
XmlSlurper is lazy and great for reading; XmlParser builds a tree of
Node objects that you can modify. XmlNodePrinter writes it back out
(XmlUtil.serialize does too, and adds an XML declaration).