Skip to content

Groovy Guide — GPath: Navigating Object Graphs

DodaTech Updated 2026-06-28 4 min read

In this tutorial, you will learn about Groovy Guide. We cover key concepts, practical examples, and best practices to help you master this topic.

GPath is Groovy's path expression language for navigating object graphs with dot notation, wildcards, and find operations, inspired by XPath but for Groovy objects.

What You'll Learn

  • Dot notation navigation
  • Spread-dot operator (*.)
  • Deep property access
  • Finding and filtering
  • GPath with XML and JSON

Why It Matters

GPath makes navigating complex nested data structures concise. What requires multiple lines of Java code becomes a single expression. Durga Antivirus Pro uses GPath for configuration traversal.

Real-World Use

Traversing API responses, navigating configuration trees, processing XML/JSON data, and querying object graphs.

flowchart LR
    A["GPath"] --> B["Dot Notation"]
    B --> C["Spread-Dot"]
    C --> D["Deep Access"]
    D --> E["XML/JSON"]
    A:::current --> B
    style A fill:#2563eb,stroke:#2563eb,color:#fff
    style B fill:#dbeafe,stroke:#2563eb,color:#1e40af
    style C fill:#dbeafe,stroke:#2563eb,color:#1e40af
    style D fill:#dbeafe,stroke:#2563eb,color:#1e40af
    style E fill:#f1f5f9,stroke:#94a3b8,color:#64748b

Basic Navigation

class Address { String city; String state }
class Person { String name; Address address }

def person = new Person(
    name: "Alice",
    address: new Address(city: "Portland", state: "OR")
)

// Simple navigation
person.name           // "Alice"
person.address.city   // "Portland"
person.address.state  // "OR"

Spread-Dot Operator

def people = [
    [name: "Alice", age: 30],
    [name: "Bob", age: 25],
    [name: "Carol", age: 35],
]

// Collect property from all elements
people*.name    // ["Alice", "Bob", "Carol"]
people*.age     // [30, 25, 35]

// Equivalent to collect
people.collect { it.name }

// Nested spread
def teams = [
    [members: [[name: "A"], [name: "B"]]],
    [members: [[name: "C"]]],
]
teams*.members*.name.flatten()  // ["A", "B", "C"]

Deep Property Access

class Team {
    String name
    List<Member> members
}
class Member {
    String name
    Map<String, String> skills
}

def team = new Team(
    name: "Dev Team",
    members: [
        new Member(name: "Alice", skills: [java: "senior", groovy: "expert"]),
        new Member(name: "Bob", skills: [java: "mid", python: "junior"]),
    ]
)

// Deep navigation
team.members*.name  // ["Alice", "Bob"]
team.members*.skills*.java  // ["senior", "mid"]
team.members.findAll { it.skills.groovy }

Finding with GPath

def numbers = [1, 2, 3, 4, 5, 6]

// Find operations
numbers.find { it > 3 }          // 4 (first match)
numbers.findAll { it % 2 == 0 }  // [2, 4, 6]
numbers.grep { it > 3 }          // [4, 5, 6]
numbers.findIndexOf { it > 3 }   // 3 (index)

// With GPath
def users = [
    [name: "Alice", roles: ["admin", "user"]],
    [name: "Bob", roles: ["user"]],
]

users.findAll { "admin" in it.roles }*.name  // ["Alice"]

GPath with XML

def xml = '''
<library>
    <book id="1">
        <title>Groovy in Action</title>
        <author>Dierk Konig</author>
    </book>
    <book id="2">
        <title>Making Java Groovy</title>
        <author>Ken Kousen</author>
    </book>
</library>
'''

def root = new XmlSlurper().parseText(xml)

// GPath navigation
root.book.title.text()        // "Groovy in Action"
root.book*.author.text()      // ["Dierk Konig", "Ken Kousen"]
root.book.findAll { it.@id == "2" }.title.text()

GPath with JSON

import groovy.json.JsonSlurper

def json = '''
{
    "users": [
        {"name": "Alice", "email": "a@example.com"},
        {"name": "Bob", "email": "b@example.com"}
    ]
}
'''

def data = new JsonSlurper().parseText(json)

// GPath navigation
data.users.name    // ["Alice", "Bob"]
data.users*.email  // ["a@example.com", "b@example.com"]
data.users.findAll { it.name.startsWith("A") }

Common Mistakes

1. NPE on null objects

Use ?. safe navigation: person?.address?.city returns null instead of throwing.

2. Spread-dot with null elements

list*.property skips null elements. Use list.findAll { it }*.property to filter nulls.

3. Deep navigation performance

Deep GPath chains create intermediate collections. For large data, break into steps.

4. GPath with maps vs objects

Map navigation uses map.key. Object navigation uses obj.property. They look the same but differ slightly.

5. XML GPath namespace issues

XML with namespaces requires namespace-aware navigation. Use declareNamespace with XmlSlurper.

Practice Questions

1. What is GPath? A path expression language for navigating object graphs using dot notation, inspired by XPath.

2. What does the spread-dot operator (*.) do? Collects the given property from all elements in a collection, returning a list of values.

3. How is GPath used with XML? XmlSlurper creates GPath-compatible objects. Navigate with dot notation: root.child.grandchild.text().

Challenge: Use GPath to extract all email addresses from a nested JSON structure.

FAQ

{{< faq question="Is GPath the same as XPath?" >} Similar concept but GPath works on Groovy objects. XPath works on XML documents. GPath is simpler for object navigation. {{< /faq >}}

{{< faq question="Can GPath modify objects?" >} Yes. GPath can assign values: person.address.city = "New York" modifies the nested object. {{< /faq >}}

{{< faq question="Does GPath work with Java objects?" >} Yes. Groovy's meta-object protocol adds GPath capabilities to all Java objects. {{< /faq >}}

{{< faq question="What is the difference between *. and .collect?" >} *. is shorthand for .collect. list*.prop is equivalent to list.collect { it.prop }. {{< /faq >}}

{{< faq question="Can GPath filter elements?" >} Not directly. Use findAll/grep/find methods combined with GPath for filtering. {{< /faq >}}

Mini Project

Extract data from a nested API response using GPath:

def response = [
    status: "ok",
    data: [
        users: [
            [id: 1, name: "Alice", profile: [email: "alice@example.com", role: "admin"]],
            [id: 2, name: "Bob", profile: [email: "bob@example.com", role: "user"]],
            [id: 3, name: "Carol", profile: [email: "carol@example.com", role: "user"]],
        ]
    ]
]

// Extract admin emails
def adminEmails = response.data.users
    .findAll { it.profile.role == "admin" }
    *.profile.email

println adminEmails  // ["alice@example.com"]

// Extract all user names
def names = response.data.users*.name
println names  // ["Alice", "Bob", "Carol"]

What's Next

Now that you understand GPath, explore Groovy's Builder pattern for constructing structures.

Topic Description Link
Groovy Builders Builder pattern in Groovy {{< ref "09-builders" >}}
Groovy XML XML processing {{< ref "10-xml" >}}

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro