Skip to content
AgileLogics
6 min read

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.book is every <book> child; [0] picks one.
  • .@id reads 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).