12 Groovy XmlSlurper Examples – Parse XML the Easy Way

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.

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:

FeatureJava DOMJava SAXGroovy XmlSlurper
Ease of useVerboseVery verboseOne-liner access
Memory usageFull tree in memoryLow (streaming)Lazy evaluation
NavigationgetChildNodes loopsEvent callbacksDot notation (GPath)
Lines for basic parse15-20 lines30+ lines2-3 lines
Namespace handlingManualManualdeclareNamespace()

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 plain String value
  • 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 GPathResult directly to a String without calling text() or toString()
  • 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 GPathResult for lazy, read-only navigation
  • Dot notation and @attribute syntax make navigation intuitive
  • Use '**' (depthFirst) to search the entire XML tree
  • Missing elements return empty GPathResult instead of null – no NPEs
  • Always call text() or toString() 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.

Previous in Series: Groovy Constructors and Named Parameters

Next in Series: Groovy XmlParser vs XmlSlurper – Which to Use

Related Topics You Might Like:

This post is part of the Groovy & Grails Cookbook series on TechnoScripts.com

RahulAuthor posts

Avatar for Rahul

Rahul is a passionate IT professional who loves to sharing his knowledge with others and inspiring them to expand their technical knowledge. Rahul's current objective is to write informative and easy-to-understand articles to help people avoid day-to-day technical issues altogether. Follow Rahul's blog to stay informed on the latest trends in IT and gain insights into how to tackle complex technical issues. Whether you're a beginner or an expert in the field, Rahul's articles are sure to leave you feeling inspired and informed.

No comment

Leave a Reply

Your email address will not be published. Required fields are marked *