ZetCode

Groovy XML

last modified October 15, 2026

Groovy treats XML as a native data type rather than a chore delegated to a heavyweight parser API. The XmlSlurper class parses a document into a tree that you navigate with GPath, a query language whose dots and stars mirror Groovy's own property access. Combined with MarkupBuilder for output, Groovy makes reading and writing XML remarkably concise. This tutorial covers both directions with practical examples.

Parsing XML with XmlSlurper

Create an XmlSlurper and call parseText to turn an XML string into a navigable tree. Child elements are reached by name, and their text is obtained with text().

SimpleXmlSlurper.groovy
import groovy.xml.XmlSlurper

def xmlText = '''<book>
    <title>Groovy in Action</title>
    <author>Dierk Koenig</author>
    <year>2015</year>
</book>'''

def book = new XmlSlurper().parseText(xmlText)

println book.title.text()
println book.author.text()
println book.year.text()

The parsed book behaves like an object whose elements are properties. book.title.text() returns the enclosed string. The result is a GPathResult, which supports many of the collection methods you already know from lists.

$ groovy SimpleXmlSlurper.groovy
Groovy in Action
Dierk Koenig
2015

Parsing XML from a File

The parse method accepts a File, a Reader, or an InputStream. This is the usual entry point when loading configuration or data from disk.

XmlFromFile.groovy
import groovy.xml.XmlSlurper

def xmlFile = new File("books.xml")
def root = new XmlSlurper().parse(xmlFile)

root.book.each { book ->
    println "${book.title.text()} (${book.year.text()})"
}

Assuming the root element is <books> containing several <book> children, the each call iterates over them. Each item is again a GPathResult, so the same navigation rules apply at every level.

$ groovy XmlFromFile.groovy
A (2001)
B (2002)

Navigating with GPath

GPath descends the tree with dots. To iterate all descendants with a given name, prefix the name with a double dot, or use the spread-style double dot to reach any depth.

GPathNavigation.groovy
import groovy.xml.XmlSlurper

def xmlText = '''<catalog>
    <book>
        <title>Book One</title>
        <prices><price currency="USD">29.99</price></prices>
    </book>
    <book>
        <title>Book Two</title>
        <prices><price currency="EUR">24.50</price></prices>
    </book>
</catalog>'''

def catalog = new XmlSlurper().parseText(xmlText)

println catalog.book.title*.text()
println catalog.'**'.findAll { it.name() == 'price' }*.text()

The spread operator on title*.text() collects the text of every title into a list. The double-star pattern ** matches descendants at any depth, and findAll narrows them by tag name. GPath turns tree queries into ordinary collection pipelines.

$ groovy GPathNavigation.groovy
[Book One, Book Two]
[29.99, 24.50]

Filtering with GPath

Because GPath nodes respond to findAll, find, and other collection methods, you filter by attribute or text directly.

GPathFilter.groovy
import groovy.xml.XmlSlurper

def xmlText = '''<books>
    <book available="true"><title>Groovy</title></book>
    <book available="false"><title>Java</title></book>
</books>'''

def books = new XmlSlurper().parseText(xmlText)

def available = books.book.findAll { it.@available == 'true' }
available.each { println it.title.text() }

The @ prefix reads an attribute, so it.@available compares the attribute value. Only the available book is printed. Attribute access composes naturally with closures and predicates.

$ groovy GPathFilter.groovy
Groovy

Reading Attributes

Attributes are accessed with @name, while the attributes() method returns them as a map when you need to enumerate them.

XmlAttributes.groovy
import groovy.xml.XmlSlurper

def xmlText = '''<img src="logo.png" width="120" height="40" />'''

def img = new XmlSlurper().parseText(xmlText)

println img.@src
println img.attributes()

img.@src yields logo.png, and attributes() returns a map of every attribute. This is convenient for generic processing where the attribute names are not known in advance.

$ groovy XmlAttributes.groovy
logo.png
[src:logo.png, width:120, height:40]

Working with Namespaces

Namespace-qualified elements are addressed by declaring a prefix with declareNamespace and using that prefix in GPath expressions.

XmlNamespace.groovy
import groovy.xml.XmlSlurper

def xmlText = '''<root xmlns:h="http://example.com/hello">
    <h:greeting>Hi there</h:greeting>
</root>'''

def root = new XmlSlurper().parseText(xmlText)
root.declareNamespace(h: 'http://example.com/hello')

println root.'h:greeting'.text()

Once the prefix is declared, h:greeting resolves against the URI. Namespaces are common in SOAP, SVG, and Atom feeds, so this keeps navigation readable without manual URI juggling.

$ groovy XmlNamespace.groovy
Hi there

XmlParser vs XmlSlurper

XmlParser returns a tree of Node objects that can be mutated and then serialized back to XML. XmlSlurper is read-only but lazier and more memory-friendly. Choose the slurper for queries and the parser when you need to edit the document.

XmlParserEdit.groovy
import groovy.xml.XmlParser
import groovy.xml.XmlNodePrinter

def xmlText = '''<book><title>Old Title</title></book>'''

def book = new XmlParser().parseText(xmlText)
book.title[0].value = ['New Title']

new XmlNodePrinter().print(book)

The parsed node is a mutable structure, so assigning to value replaces the title text. XmlNodePrinter then renders the updated tree. This round trip is ideal for in-place transformations.

$ groovy XmlParserEdit.groovy
<book>
  <title>
    New Title
  </title>
</book>

Building XML with MarkupBuilder

MarkupBuilder generates XML from a builder-style closure. Nested closure calls become nested elements, and named arguments become attributes.

MarkupBuilderDemo.groovy
import groovy.xml.MarkupBuilder

def writer = new StringWriter()
def xml = new MarkupBuilder(writer)

xml.books {
    book(available: 'true') {
        title('Groovy in Action')
        year(2015)
    }
    book(available: 'false') {
        title('Effective Java')
        year(2018)
    }
}

println writer.toString()

The books closure contains two book elements, each carrying an available attribute and nested children. The MarkupBuilder handles escaping and indentation, producing clean XML without string concatenation.

$ groovy MarkupBuilderDemo.groovy
<books>
  <book available='true'>
    <title>Groovy in Action</title>
    <year>2015</year>
  </book>
  <book available='false'>
    <title>Effective Java</title>
    <year>2018</year>
  </book>
</books>

Writing XML to a File

Pass a FileWriter to MarkupBuilder to persist the generated document directly, which is convenient for feeds and reports.

WriteXmlFile.groovy
import groovy.xml.MarkupBuilder

def file = new File("catalog.xml")
file.withWriter('UTF-8') { writer ->
    def xml = new MarkupBuilder(writer)
    xml.catalog {
        item(id: 1, name: 'Widget')
        item(id: 2, name: 'Gadget')
    }
}

println file.text

The withWriter block closes the stream automatically, then file.text confirms the result. Writing XML this way avoids manual resource management and keeps the document structure visible in code.

$ groovy WriteXmlFile.groovy
<catalog>
  <item id='1' name='Widget' />
  <item id='2' name='Gadget' />
</catalog>

Best Practices for Working with XML

Source

Groovy XML Processing Documentation

In this tutorial, we parsed and navigated XML with XmlSlurper and GPath, filtered by attributes, handled namespaces, edited documents with XmlParser, and generated XML with MarkupBuilder. Groovy's native XML support keeps both reading and writing close to the shape of the data itself.

Author

My name is Jan Bodnar, and I am a passionate programmer with many years of programming experience. I have been writing programming articles since 2007. So far, I have written over 1400 articles and 8 e-books. I have over eight years of experience in teaching programming.

List all Groovy tutorials.