Groovy XmlSlurper is explored here with 12 practical examples covering XML parsing, namespaces, attributes, GPath queries, and real-world patterns. Tested on Groovy 5.x.
“XML is like violence – if it doesn’t solve your problems, you’re not using enough of it.”
Tim Berners-Lee, Web Standards Pioneer
Last Updated: March 2026 | Tested on: Groovy 5.x, Java 17+ | Difficulty: Intermediate | Reading Time: 20 minutes
If you’ve wrestled with DOM parsers, SAX handlers, or JAXB annotations just to pull a few values out of an XML document, groovy xmlslurper is going to feel like a different language entirely. It lets you parse XML and navigate it with simple dot notation – no factory configurations, no handler callbacks, no casting.
With Groovy parse XML capabilities through XmlSlurper, you can read an entire XML document and navigate it using simple dot notation – just like accessing properties on a regular Groovy object. No factory configurations, no handler callbacks, no casting. You just slurp the XML and start reading values.
In this post, we’ll cover the key techniques for using XmlSlurper: how it works under the hood, 12 hands-on examples with tested output, namespace handling, attribute access, GPath queries, and the pitfalls that catch developers off guard. If you want to compare XmlSlurper with XmlParser, check out Groovy XmlParser vs XmlSlurper. And when you’re ready to create or modify XML, head over to Groovy Create and Modify XML.
Table of Contents
What Is XmlSlurper?
groovy.xml.XmlSlurper is a Groovy class that parses XML documents and returns a GPathResult object. Unlike Java’s DOM parser which builds a full in-memory tree of Node objects, XmlSlurper takes a lazy evaluation approach. It parses the XML using SAX internally, then lets you navigate the result using Groovy’s GPath expressions.
According to the official Groovy XML processing documentation, XmlSlurper is designed for scenarios where you need to read XML data without modifying it. It’s memory-efficient and intuitive.
Key Points:
- XmlSlurper returns a
GPathResult, not a DOM tree - It uses lazy evaluation – nodes are resolved only when accessed
- Navigation uses dot notation and GPath expressions
- It parses from strings, files, URLs, streams, and readers
- Namespace-aware by default
- Not ideal for modifying XML in-place – use XmlParser or MarkupBuilder for that
Why Use XmlSlurper for XML Parsing?
Java gives you at least four ways to parse XML – DOM, SAX, StAX, and JAXB. Every one of them involves boilerplate. Here’s why Groovy developers reach for XmlSlurper instead:
| Feature | Java DOM | Java SAX | Groovy XmlSlurper |
|---|---|---|---|
| Ease of use | Verbose | Very verbose | One-liner access |
| Memory usage | Full tree in memory | Low (streaming) | Lazy evaluation |
| Navigation | getChildNodes loops | Event callbacks | Dot notation (GPath) |
| Lines for basic parse | 15-20 lines | 30+ lines | 2-3 lines |
| Namespace handling | Manual | Manual | declareNamespace() |
The biggest win is readability. When your XML has a <book> element with a <title> child, you just write xml.book.title. No casting, no null checks, no iterator loops.
Syntax and Basic Usage
Basic Parsing Syntax
XmlSlurper Basic Syntax
// Parse from a string
def xml = new XmlSlurper().parseText(xmlString)
// Parse from a file
def xml2 = new XmlSlurper().parse(new File('data.xml'))
// Parse from a URL
def xml3 = new XmlSlurper().parse('https://example.com/data.xml')
// Parse from an InputStream
def xml4 = new XmlSlurper().parse(inputStream)
GPath Navigation
GPath Navigation Basics
def xmlText = '''
<library>
<book id="1">
<title>Groovy in Action</title>
<author>Dierk Koenig</author>
</book>
</library>
'''
def library = new XmlSlurper().parseText(xmlText)
// Dot notation to access child elements
println library.book.title // Element text
println library.book.@id // Attribute value
println library.book.author.text() // Explicit text() call
Output
Groovy in Action 1 Dierk Koenig
The parseText() method returns a GPathResult. From there, every child element is accessible as a property. Attributes use the @ prefix. And text() extracts the text content of any element.
12 Practical XmlSlurper Examples
Example 1: Parse a Simple XML String
What we’re doing: Parsing a basic XML string and reading element values.
Example 1: Simple XML Parsing
import groovy.xml.XmlSlurper
def xmlText = '''
<person>
<name>Alice</name>
<age>30</age>
<city>New York</city>
</person>
'''
def person = new XmlSlurper().parseText(xmlText)
println "Name: ${person.name}"
println "Age: ${person.age}"
println "City: ${person.city}"
println "Class: ${person.getClass().name}"
Output
Name: Alice Age: 30 City: New York Class: groovy.xml.slurpersupport.NodeChild
Import Note: In Groovy 4.x and later, groovy.xml.XmlSlurper is no longer auto-imported, so you need the import groovy.xml.XmlSlurper statement shown above. The remaining examples in this post omit the import for brevity. In Groovy 3.x and earlier, this import was automatic.
What happened here: parseText() takes the XML string and returns a GPathResult. Each child element is accessible using dot notation. The result type is NodeChild, a subclass of GPathResult. When you print it, Groovy automatically calls text() to get the string value.
Example 2: Access XML Attributes
What we’re doing: Reading attributes from XML elements using the @ syntax.
Example 2: XML Attributes
import groovy.xml.*
def xmlText = '''
<catalog>
<product id="P001" category="electronics">
<name>Laptop</name>
<price currency="USD">999.99</price>
</product>
<product id="P002" category="books">
<name>Groovy Cookbook</name>
<price currency="USD">49.99</price>
</product>
</catalog>
'''
def catalog = new XmlSlurper().parseText(xmlText)
catalog.product.each { product ->
println "ID: ${product.@id}"
println "Category: ${product.@category}"
println "Name: ${product.name}"
println "Price: ${product.price} ${product.price.@currency}"
println "---"
}
Output
ID: P001 Category: electronics Name: Laptop Price: 999.99 USD --- ID: P002 Category: books Name: Groovy Cookbook Price: 49.99 USD ---
What happened here: The @ prefix accesses XML attributes. You can use it on any element – product.@id reads the id attribute of the product element. When there are multiple sibling elements with the same name, each() iterates over them naturally.
Example 3: Iterate Over Repeated Elements
What we’re doing: Looping through multiple child elements and collecting data.
Example 3: Iterating Elements
import groovy.xml.*
def xmlText = '''
<team>
<member role="developer">Alice</member>
<member role="designer">Bob</member>
<member role="tester">Charlie</member>
<member role="developer">Diana</member>
<member role="manager">Eve</member>
</team>
'''
def team = new XmlSlurper().parseText(xmlText)
// Count members
println "Team size: ${team.member.size()}"
// Collect all names
def names = team.member.collect { it.text() }
println "Members: ${names}"
// Find all developers
def devs = team.member.findAll { it.@role == 'developer' }
println "Developers: ${devs.collect { it.text() }}"
// Get roles as a unique list
def roles = team.member.collect { it.@role.toString() }.unique()
println "Roles: ${roles}"
Output
Team size: 5 Members: [Alice, Bob, Charlie, Diana, Eve] Developers: [Alice, Diana] Roles: [developer, designer, tester, manager]
What happened here: XmlSlurper results support Groovy collection methods – size(), collect(), findAll(), each(), and more. This is the real power of GPath – you can query XML the same way you query lists and maps in Groovy.
Example 4: Nested Element Navigation
What we’re doing: Navigating deeply nested XML structures with dot notation.
Example 4: Nested Navigation
import groovy.xml.*
def xmlText = '''
<company>
<department name="Engineering">
<team name="Backend">
<lead>Alice</lead>
<members>
<member>Bob</member>
<member>Charlie</member>
</members>
</team>
<team name="Frontend">
<lead>Diana</lead>
<members>
<member>Eve</member>
<member>Frank</member>
</members>
</team>
</department>
</company>
'''
def company = new XmlSlurper().parseText(xmlText)
// Access nested elements with dot notation
println "Department: ${company.department.@name}"
company.department.team.each { team ->
println "\nTeam: ${team.@name}"
println "Lead: ${team.lead}"
println "Members:"
team.members.member.each { member ->
println " - ${member.text()}"
}
}
Output
Department: Engineering Team: Backend Lead: Alice Members: - Bob - Charlie Team: Frontend Lead: Diana Members: - Eve - Frank
What happened here: Dot notation chains let you drill into any depth: company.department.team.members.member. Each level returns a GPathResult that you can further navigate or iterate.
Example 5: GPath depthFirst and breadthFirst
What we’re doing: Using depthFirst() and breadthFirst() to search across the entire XML tree.
Example 5: depthFirst and breadthFirst
import groovy.xml.*
def xmlText = '''
<store>
<department name="Electronics">
<item>Laptop</item>
<item>Phone</item>
</department>
<department name="Books">
<item>Groovy in Action</item>
<item>Clean Code</item>
</department>
</store>
'''
def store = new XmlSlurper().parseText(xmlText)
// depthFirst() - find ALL elements named 'item' at any depth
// Using the '**' shorthand (same as depthFirst)
def allItems = store.'**'.findAll { it.name() == 'item' }
println "All items (depthFirst): ${allItems.collect { it.text() }}"
// breadthFirst() - same result, different traversal order
def allItemsBF = store.breadthFirst().findAll { it.name() == 'item' }
println "All items (breadthFirst): ${allItemsBF.collect { it.text() }}"
// Find all elements regardless of name at any depth
def allElements = store.'**'.findAll { it.name() != '' }
println "Total elements: ${allElements.size()}"
println "Element names: ${allElements.collect { it.name() }.unique()}"
Output
All items (depthFirst): [Laptop, Phone, Groovy in Action, Clean Code] All items (breadthFirst): [Laptop, Phone, Groovy in Action, Clean Code] Total elements: 6 Element names: [department, item]
What happened here: The '**' operator is shorthand for depthFirst(). It searches the entire tree for matching elements. This is incredibly useful when you don’t know the exact path to an element or need to find all occurrences across different branches.
Example 6: Filtering with findAll and find
What we’re doing: Using Groovy’s collection methods to filter XML elements by conditions.
Example 6: Filtering XML Elements
import groovy.xml.*
def xmlText = '''
<inventory>
<product id="1" status="active">
<name>Widget A</name>
<price>25.00</price>
<stock>150</stock>
</product>
<product id="2" status="discontinued">
<name>Widget B</name>
<price>15.00</price>
<stock>0</stock>
</product>
<product id="3" status="active">
<name>Widget C</name>
<price>42.50</price>
<stock>75</stock>
</product>
<product id="4" status="active">
<name>Widget D</name>
<price>8.99</price>
<stock>300</stock>
</product>
</inventory>
'''
def inventory = new XmlSlurper().parseText(xmlText)
// Find active products
def active = inventory.product.findAll { it.@status == 'active' }
println "Active products: ${active.collect { it.name.text() }}"
// Find products over $20
def expensive = inventory.product.findAll {
it.price.text().toBigDecimal() > 20.00
}
println "Over \$20: ${expensive.collect { it.name.text() }}"
// Find first out-of-stock product
def outOfStock = inventory.product.find { it.stock.text().toInteger() == 0 }
println "Out of stock: ${outOfStock?.name}"
// Sum all prices of active products
def totalValue = active.collect { it.price.text().toBigDecimal() }.sum()
println "Total active inventory value: \$${totalValue}"
Output
Active products: [Widget A, Widget C, Widget D] Over $20: [Widget A, Widget C] Out of stock: Widget B Total active inventory value: $76.49
What happened here: Since GPathResult supports Groovy collection operations, you can use findAll(), find(), collect(), and sum() just like you would on a regular list. Remember that element text is always a GPathResult, so convert it with text() and then to your desired type for numeric comparisons.
Example 7: Handling XML Namespaces
What we’re doing: Parsing namespace-qualified XML elements with declareNamespace().
Example 7: XML Namespaces
import groovy.xml.*
def xmlText = '''
<root xmlns:app="http://example.com/app"
xmlns:db="http://example.com/db">
<app:config>
<app:setting name="timeout">30</app:setting>
<app:setting name="retries">3</app:setting>
</app:config>
<db:connection>
<db:host>localhost</db:host>
<db:port>5432</db:port>
</db:connection>
</root>
'''
def root = new XmlSlurper().parseText(xmlText)
.declareNamespace(
app: 'http://example.com/app',
db: 'http://example.com/db'
)
// Access namespace-qualified elements
println "Config settings:"
root.'app:config'.'app:setting'.each { setting ->
println " ${setting.@name} = ${setting.text()}"
}
println "\nDatabase connection:"
println " Host: ${root.'db:connection'.'db:host'}"
println " Port: ${root.'db:connection'.'db:port'}"
Output
Config settings: timeout = 30 retries = 3 Database connection: Host: localhost Port: 5432
What happened here: When XML uses namespaces, you call declareNamespace() on the parsed result to register namespace prefixes. Then use quoted property access with the prefix – root.'app:config'. Without declaring namespaces, you’d need to use the full namespace URI.
Example 8: Parse XML from a File
What we’re doing: Reading and parsing an XML file from disk.
Example 8: Parsing from a File
import groovy.xml.*
// First, create a sample XML file
def xmlContent = '''<?xml version="1.0" encoding="UTF-8"?>
<employees>
<employee id="E001">
<name>Alice Johnson</name>
<department>Engineering</department>
<salary>95000</salary>
</employee>
<employee id="E002">
<name>Bob Smith</name>
<department>Marketing</department>
<salary>72000</salary>
</employee>
<employee id="E003">
<name>Charlie Brown</name>
<department>Engineering</department>
<salary>88000</salary>
</employee>
</employees>
'''
// Write XML to a temp file
def tempFile = File.createTempFile('employees', '.xml')
tempFile.text = xmlContent
tempFile.deleteOnExit()
// Parse the file
def employees = new XmlSlurper().parse(tempFile)
println "Total employees: ${employees.employee.size()}"
employees.employee.each { emp ->
println "${emp.@id}: ${emp.name} (${emp.department}) - \$${emp.salary}"
}
// Calculate average salary
def avgSalary = employees.employee.collect {
it.salary.text().toInteger()
}.average()
println "\nAverage salary: \$${avgSalary}"
Output
Total employees: 3 E001: Alice Johnson (Engineering) - $95000 E002: Bob Smith (Marketing) - $72000 E003: Charlie Brown (Engineering) - $88000 Average salary: $85000.0
What happened here: new XmlSlurper().parse(file) reads and parses the XML file in one step. The returned GPathResult works identically to parseText(). You can then apply Groovy collection methods like collect() and average() for quick analysis.
Example 9: Convert XML to Map and List
What we’re doing: Transforming XML data into Groovy maps and lists for easier processing.
Example 9: XML to Map/List
import groovy.xml.*
def xmlText = '''
<config>
<database>
<host>localhost</host>
<port>3306</port>
<name>myapp_db</name>
<credentials>
<username>admin</username>
<password>secret123</password>
</credentials>
</database>
<features>
<feature enabled="true">caching</feature>
<feature enabled="false">dark-mode</feature>
<feature enabled="true">notifications</feature>
</features>
</config>
'''
def config = new XmlSlurper().parseText(xmlText)
// Convert database config to a Map
def dbConfig = [
host : config.database.host.text(),
port : config.database.port.text().toInteger(),
name : config.database.name.text(),
username: config.database.credentials.username.text(),
password: config.database.credentials.password.text()
]
println "DB Config Map: ${dbConfig}"
// Convert features to a list of maps
def features = config.features.feature.collect { f ->
[name: f.text(), enabled: f.@enabled.toString().toBoolean()]
}
println "Features: ${features}"
// Filter enabled features
def enabledFeatures = features.findAll { it.enabled }.collect { it.name }
println "Enabled: ${enabledFeatures}"
Output
DB Config Map: [host:localhost, port:3306, name:myapp_db, username:admin, password:secret123] Features: [[name:caching, enabled:true], [name:dark-mode, enabled:false], [name:notifications, enabled:true]] Enabled: [caching, notifications]
What happened here: Converting XML to Groovy maps and lists is a common pattern. Once you have the data in native Groovy collections, you can serialize to JSON, pass to other methods, or store in databases. Remember to call text() and type conversion methods to get plain values.
Example 10: Parse XML with Mixed Content and CDATA
What we’re doing: Handling XML elements that contain mixed text and CDATA sections.
Example 10: CDATA and Mixed Content
import groovy.xml.*
def xmlText = '''
<articles>
<article id="1">
<title>Getting Started with Groovy</title>
<body><![CDATA[
Use <code> tags for inline code.
Special chars like < and > are safe here.
]]></body>
<tags>
<tag>groovy</tag>
<tag>tutorial</tag>
<tag>beginner</tag>
</tags>
</article>
<article id="2">
<title>Advanced XML Parsing</title>
<body><![CDATA[
XmlSlurper handles <xml> within CDATA gracefully.
]]></body>
<tags>
<tag>groovy</tag>
<tag>xml</tag>
</tags>
</article>
</articles>
'''
def articles = new XmlSlurper().parseText(xmlText)
articles.article.each { article ->
println "Article #${article.@id}: ${article.title}"
println "Body preview: ${article.body.text().trim().take(50)}..."
println "Tags: ${article.tags.tag.collect { it.text() }.join(', ')}"
println()
}
// Count articles with 'groovy' tag
def groovyArticles = articles.article.findAll { article ->
article.tags.tag.any { it.text() == 'groovy' }
}
println "Articles tagged 'groovy': ${groovyArticles.size()}"
Output
Article #1: Getting Started with Groovy
Body preview: Use <code> tags for inline code.
Special ch...
Tags: groovy, tutorial, beginner
Article #2: Advanced XML Parsing
Body preview: XmlSlurper handles <xml> within CDATA gracefull...
Tags: groovy, xml
Articles tagged 'groovy': 2
What happened here: XmlSlurper handles CDATA sections transparently. When you call text() on an element containing CDATA, you get the raw text content. The angle brackets inside CDATA are preserved as literal characters. The any() method is great for checking if at least one child element matches a condition.
Example 11: XmlSlurper with children() and parent()
What we’re doing: Navigating the XML tree using children(), parent(), and name() methods.
Example 11: Tree Navigation
import groovy.xml.*
def xmlText = '''
<library>
<section name="Fiction">
<book>The Great Gatsby</book>
<book>1984</book>
</section>
<section name="Non-Fiction">
<book>Sapiens</book>
<book>Thinking Fast and Slow</book>
<book>Atomic Habits</book>
</section>
</library>
'''
def library = new XmlSlurper().parseText(xmlText)
// children() returns all direct child elements
println "Library sections: ${library.children().size()}"
library.children().each { section ->
println " ${section.@name}: ${section.children().size()} books"
}
// name() returns the element name
println "\nRoot element name: ${library.name()}"
// Iterate all children of all sections
library.section.each { section ->
section.children().each { book ->
println "${book.name()}: ${book.text()} (in ${section.@name})"
}
}
Output
Library sections: 2 Fiction: 2 books Non-Fiction: 3 books Root element name: library book: The Great Gatsby (in Fiction) book: 1984 (in Fiction) book: Sapiens (in Non-Fiction) book: Thinking Fast and Slow (in Non-Fiction) book: Atomic Habits (in Non-Fiction)
What happened here: children() returns all direct child elements as a GPathResult. This is useful when you don’t know the child element names in advance, or when elements have different names. name() returns the tag name of the current element.
Example 12: Real-World API XML Response Parsing
What we’re doing: Parsing a realistic API XML response with error handling.
Example 12: API Response Parsing
import groovy.xml.*
def xmlResponse = '''
<api-response status="success" timestamp="2026-03-08T10:30:00Z">
<pagination total="3" page="1" per-page="10"/>
<users>
<user active="true">
<id>101</id>
<username>alice_dev</username>
<email>alice@example.com</email>
<roles>
<role>admin</role>
<role>developer</role>
</roles>
</user>
<user active="true">
<id>102</id>
<username>bob_qa</username>
<email>bob@example.com</email>
<roles>
<role>tester</role>
</roles>
</user>
<user active="false">
<id>103</id>
<username>charlie_old</username>
<email>charlie@example.com</email>
<roles>
<role>viewer</role>
</roles>
</user>
</users>
</api-response>
'''
def response = new XmlSlurper().parseText(xmlResponse)
// Check response status
def status = response.@status.toString()
println "API Status: ${status}"
println "Timestamp: ${response.@timestamp}"
// Pagination info (self-closing element with attributes)
def pagination = response.pagination
println "Total: ${pagination.@total}, Page: ${pagination.@page}"
// Parse users into a structured list
def users = response.users.user.collect { u ->
[
id : u.id.text().toInteger(),
username: u.username.text(),
email : u.email.text(),
active : u.@active.toString().toBoolean(),
roles : u.roles.role.collect { it.text() }
]
}
users.each { println it }
// Find admins
def admins = users.findAll { 'admin' in it.roles }
println "\nAdmins: ${admins.collect { it.username }}"
// Count active users
println "Active users: ${users.count { it.active }}"
Output
API Status: success Timestamp: 2026-03-08T10:30:00Z Total: 3, Page: 1 [id:101, username:alice_dev, email:alice@example.com, active:true, roles:[admin, developer]] [id:102, username:bob_qa, email:bob@example.com, active:true, roles:[tester]] [id:103, username:charlie_old, email:charlie@example.com, active:false, roles:[viewer]] Admins: [alice_dev] Active users: 2
What happened here: This example shows a realistic workflow. You parse the XML response, check status attributes, read pagination from a self-closing element, then transform the user data into Groovy maps for further processing. Converting early from GPathResult to native types makes downstream logic cleaner.
Edge Cases and Best Practices
Handling Missing Elements
Missing Elements
def xmlText = '''
<person>
<name>Alice</name>
</person>
'''
def person = new XmlSlurper().parseText(xmlText)
// Accessing a non-existent element does NOT throw an exception
println "Email: '${person.email}'"
println "Email is empty: ${person.email.isEmpty()}"
println "Email text: '${person.email.text()}'"
// Safe way to check for existence
if (!person.email.isEmpty()) {
println "Has email"
} else {
println "No email element found"
}
// Non-existent attribute
println "Age attr: '${person.@age}'"
Output
Email: '' Email is empty: true Email text: '' No email element found Age attr: ''
This is an important detail. XmlSlurper never throws a NullPointerException for missing elements. It returns an empty GPathResult instead. Use isEmpty() to check whether an element actually exists in the XML.
Best Practices Summary
DO:
- Use
text()explicitly when you need a plainStringvalue - Use
isEmpty()to check for missing elements before processing - Convert attribute values with
.toString()before comparisons - Use
'**'(depthFirst) when you need to search across the entire document - Prefer XmlSlurper for read-only XML processing
DON’T:
- Use XmlSlurper to modify XML – use XmlParser or MarkupBuilder instead
- Compare
GPathResultdirectly to a String without callingtext()ortoString() - Forget to handle external entity expansion (XXE) for untrusted XML input
Common Pitfalls
Pitfall 1: GPathResult is Not a String
GPathResult vs String
def xmlText = '<item><name>Widget</name></item>'
def item = new XmlSlurper().parseText(xmlText)
// This comparison works (Groovy coerces GPathResult)
println "GPath == 'Widget': ${item.name == 'Widget'}"
// But this fails - type check
println "Is String: ${item.name instanceof String}"
println "Is GPathResult: ${item.name instanceof groovy.xml.slurpersupport.GPathResult}"
// Always call text() when you need a real String
def name = item.name.text()
println "text() is String: ${name instanceof String}"
// Important for Map keys and switch statements
def map = [(item.name.text()): 'found']
println "Map lookup: ${map['Widget']}"
Output
GPath == 'Widget': true Is String: false Is GPathResult: true text() is String: true Map lookup: found
Even though == works thanks to Groovy’s type coercion, the underlying type is still GPathResult. This matters when you use the value as a Map key, pass it to a Java method expecting String, or compare types.
Pitfall 2: XXE (XML External Entity) Vulnerability
Disabling XXE
// SECURE: Disable external entities for untrusted XML
def secureSlurper = new XmlSlurper()
secureSlurper.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true)
secureSlurper.setFeature("http://xml.org/sax/features/external-general-entities", false)
secureSlurper.setFeature("http://xml.org/sax/features/external-parameter-entities", false)
def safeXml = '<data><value>safe content</value></data>'
def result = secureSlurper.parseText(safeXml)
println "Secure parse: ${result.value}"
Output
Secure parse: safe content
When parsing XML from external sources (user input, APIs, uploaded files), always disable external entity processing. XXE attacks can read local files, make network requests, or cause denial-of-service.
Pitfall 3: Element Name Conflicts with GPath Methods
Name Conflicts
def xmlText = '''
<data>
<name>Test</name>
<text>Some content</text>
<size>42</size>
</data>
'''
def data = new XmlSlurper().parseText(xmlText)
// 'name' conflicts with GPathResult.name() method
// 'text' conflicts with GPathResult.text() method
// Both work as element access in most cases:
println "name element: ${data.name}"
println "text element: ${data.text}"
// But if you need the actual GPath method:
println "Root element name: ${data.name()}" // calls the method
println "Root element text: ${data.text()}" // calls the method
Output
name element: Test text element: Some content Root element name: data Root element text: TestSome content42
When your XML has elements named <name>, <text>, or <size>, they can clash with built-in GPathResult methods. Without parentheses, Groovy treats them as element access. With parentheses, they call the method. Be aware of this distinction.
Conclusion
We covered Groovy XmlSlurper from basic parsing to real-world API response handling, including namespaces, attributes, GPath searches, and security considerations. If you’ve ever suffered through Java’s XML parsing APIs, XmlSlurper is a genuine productivity boost.
The key things to remember: XmlSlurper returns GPathResult objects (not Strings), use dot notation for navigation, @ for attributes, '**' for deep searches, and always call text() when you need actual string values. For untrusted XML, disable external entities.
Next, learn the differences between XmlSlurper and XmlParser in XmlParser vs XmlSlurper – Which to Use, and then move on to creating and modifying XML documents.
Summary
- XmlSlurper parses XML and returns
GPathResultfor lazy, read-only navigation - Dot notation and
@attributesyntax make navigation intuitive - Use
'**'(depthFirst) to search the entire XML tree - Missing elements return empty GPathResult instead of null – no NPEs
- Always call
text()ortoString()when you need a plain String
If you also work with build tools, CI/CD pipelines, or cloud CLIs, check out Command Playground to practice 105+ CLI tools directly in your browser — no install needed.
Up next: Groovy XmlParser vs XmlSlurper – Which to Use
Frequently Asked Questions
What is the difference between XmlSlurper and XmlParser in Groovy?
XmlSlurper returns a GPathResult with lazy evaluation and is best for read-only XML access. XmlParser returns a Node tree that can be modified in-place. XmlSlurper is more memory-efficient for large documents, while XmlParser is better when you need to modify the XML structure. See our detailed comparison at XmlParser vs XmlSlurper.
How do I access XML attributes with XmlSlurper?
Use the @ prefix to access attributes. For example, if your XML has <product id="123">, you access it with product.@id. The returned value is a GPathResult, so call .toString() if you need a plain String.
Does XmlSlurper throw an exception for missing elements?
No. XmlSlurper returns an empty GPathResult when you access a non-existent element. This means you won’t get NullPointerException. Use the isEmpty() method to check whether an element actually exists in the XML document.
How do I handle XML namespaces with XmlSlurper?
Call declareNamespace() on the parsed result to register namespace prefixes. Then access elements using quoted property syntax like root.’ns:element’. Alternatively, you can use the namespace-agnostic approach by accessing elements without the prefix if no conflicts exist.
Can I use XmlSlurper to parse large XML files?
XmlSlurper uses SAX parsing internally and lazy evaluation, making it more memory-efficient than DOM-based parsers. However, it still loads the document structure. For very large files (hundreds of MB), consider using a streaming StAX parser or processing the file in chunks.
Related Posts
Previous in Series: Groovy Constructors and Named Parameters
Next in Series: Groovy XmlParser vs XmlSlurper – Which to Use
Related Topics You Might Like:
- Groovy XmlParser vs XmlSlurper – Which to Use
- Groovy Create and Modify XML Documents
- Groovy findAll Method – Filter Collections Like a Pro
This post is part of the Groovy & Grails Cookbook series on TechnoScripts.com

No comment