CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-codehaus-groovy--groovy-xml

XML processing utilities for Apache Groovy including markup builders, parsers, and navigation tools

Pending
Overview
Eval results
Files

navigation.mddocs/

XML Navigation

The GPathResult hierarchy provides XPath-like navigation through parsed XML documents from XmlSlurper, offering powerful querying and traversal capabilities with lazy evaluation.

GPathResult

The base class for all XML navigation results, providing core navigation and querying functionality.

public abstract class GPathResult implements Writable, Buildable, Iterable<GPathResult> {
    // Navigation methods
    public GPathResult parent();
    public GPathResult children();
    public GPathResult pop();
    public String name();
    public String lookupNamespace(String prefix);
    
    // Content access
    public String toString();
    public abstract String text();
    public abstract int size();
    public boolean isEmpty();
    
    // Type conversions
    public Integer toInteger();
    public Long toLong();
    public Float toFloat();
    public Double toDouble();
    public BigDecimal toBigDecimal();
    public BigInteger toBigInteger();
    public URL toURL() throws MalformedURLException;
    public URI toURI() throws URISyntaxException;
    public Boolean toBoolean();
    
    // Collection operations
    public Object getAt(int index);
    public Object getAt(IntRange range);
    public void putAt(int index, Object newValue);
    public List<GPathResult> list();
    public Iterator<GPathResult> iterator();
    public Iterator<GPathResult> depthFirst();
    public Iterator<GPathResult> breadthFirst();
    
    // Content modification (creates new structures)
    public Object leftShift(Object newValue);
    public Object plus(Object newValue);
    public GPathResult declareNamespace(Map<String, String> newNamespaceMapping);
    
    // Filtering and searching
    public abstract GPathResult find(Closure closure);
    public abstract GPathResult findAll(Closure closure);
    
    // Utility methods
    public void writeTo(Writer out) throws IOException;
    public boolean asBoolean();
}

Navigation Patterns

Basic Element Navigation

def slurper = new XmlSlurper()
def catalog = slurper.parseText('''
    <catalog>
        <books>
            <book id="1">
                <title>The Great Gatsby</title>
                <author>F. Scott Fitzgerald</author>
                <genres>
                    <genre>Classic</genre>
                    <genre>Fiction</genre>
                </genres>
            </book>
            <book id="2">
                <title>1984</title>
                <author>George Orwell</author>
                <genres>
                    <genre>Dystopian</genre>
                    <genre>Political</genre>
                </genres>
            </book>
        </books>
    </catalog>
''')

// Direct navigation
println catalog.books.book.title           // All book titles
println catalog.books.book[0].title        // First book title
println catalog.books.book.size()          // Number of books

// Attribute access
println catalog.books.book.'@id'           // All book IDs
println catalog.books.book[0].'@id'        // First book's ID

// Nested navigation
println catalog.books.book.genres.genre    // All genres from all books
println catalog.books.book[0].genres.genre.text()  // Genres of first book

Deep Navigation with ** Operator

// Find all elements with a specific name anywhere in the tree
println catalog.'**'.findAll { it.name() == 'genre' }  // All genre elements
println catalog.'**'.title                             // All titles at any level
println catalog.'**'.findAll { it.'@id' }              // All elements with id attribute

// Complex deep searches
def allTextNodes = catalog.'**'.findAll { 
    it.text() && !it.children() 
}  // All leaf text nodes

def allElements = catalog.'**'.findAll { 
    it.name() 
}  // All elements (non-text nodes)

Filtering and Searching

// Find elements by attribute values
def book1 = catalog.books.book.find { it.'@id' == '1' }
def classicBooks = catalog.books.book.findAll { 
    it.genres.genre.find { it.text() == 'Classic' }
}

// Find by content
def orwellBooks = catalog.books.book.findAll { 
    it.author.text().contains('Orwell') 
}

// Complex filtering
def longTitles = catalog.books.book.findAll { 
    it.title.text().length() > 10 
}

// Combine filters
def fictionByFitzgerald = catalog.books.book.findAll { book ->
    book.author.text().contains('Fitzgerald') && 
    book.genres.genre.find { it.text() == 'Fiction' }
}

GPathResult Implementations

Node

Represents a single XML element in the navigation result.

public class Node extends GPathResult {
    public Node(Node parent, String name, Map<String, String> attributes, 
                Map<String, String> attributeNamespaces, Map<String, String> namespaceTagHints);
    
    @Override
    public String text();
    @Override
    public int size();
    @Override
    public GPathResult find(Closure closure);
    @Override
    public GPathResult findAll(Closure closure);
}

NodeChild

Represents a child element accessed from a parent node.

public class NodeChild extends GPathResult {
    public NodeChild(Node node, GPathResult parent, String namespacePrefix, 
                     Map<String, String> namespaceTagHints);
    
    @Override
    public String text();
    @Override
    public int size();
    @Override
    public GPathResult find(Closure closure);
    @Override
    public GPathResult findAll(Closure closure);
}

NodeChildren

Represents a collection of child elements.

public class NodeChildren extends GPathResult {
    public NodeChildren(Node parent, String name, String namespacePrefix, 
                        Map<String, String> namespaceTagHints);
    
    @Override
    public String text();
    @Override
    public int size();
    @Override
    public GPathResult find(Closure closure);
    @Override
    public GPathResult findAll(Closure closure);
}

Attributes

Provides access to XML attributes as navigable results.

public class Attributes extends GPathResult {
    public Attributes(Node parent, String name, String namespacePrefix, 
                      Map<String, String> namespaceTagHints);
    
    @Override
    public String text();
    @Override
    public int size();
    @Override
    public GPathResult find(Closure closure);
    @Override
    public GPathResult findAll(Closure closure);
}

Advanced Navigation Examples

Iterating Through Results

// Iterate over all books
catalog.books.book.each { book ->
    println "Book: ${book.title} by ${book.author}"
    println "ID: ${book.'@id'}"
    book.genres.genre.each { genre ->
        println "  Genre: ${genre}"
    }
}

// Using iterator explicitly
def bookIterator = catalog.books.book.iterator()
while (bookIterator.hasNext()) {
    def book = bookIterator.next()
    println book.title.text()
}

// Depth-first traversal
catalog.depthFirst().each { node ->
    if (node.text() && !node.children()) {
        println "Leaf node: ${node.name()} = ${node.text()}"
    }
}

Content Type Conversions

def prices = slurper.parseText('''
    <products>
        <product price="12.99">Book</product>
        <product price="25.50">Magazine</product>
        <product active="true">Subscription</product>
        <product count="42">Bundle</product>
    </products>
''')

// Convert to different types
prices.product.each { product ->
    if (product.'@price') {
        def price = product.'@price'.toDouble()
        println "Price: \$${price}"
    }
    if (product.'@active') {
        def active = product.'@active'.toBoolean()
        println "Active: ${active}"
    }
    if (product.'@count') {
        def count = product.'@count'.toInteger()
        println "Count: ${count}"
    }
}

Working with Namespaces

def nsXml = '''
    <root xmlns:book="http://example.com/book" 
          xmlns:author="http://example.com/author">
        <book:catalog>
            <book:item>
                <book:title>Sample Book</book:title>
                <author:name>John Doe</author:name>
            </book:item>
        </book:catalog>
    </root>
'''

def nsResult = slurper.parseText(nsXml)

// Access namespaced elements
println nsResult.'book:catalog'.'book:item'.'book:title'
println nsResult.'book:catalog'.'book:item'.'author:name'

// Declare namespace mappings for easier access
def mapped = nsResult.declareNamespace([
    'b': 'http://example.com/book',
    'a': 'http://example.com/author'
])

println mapped.'b:catalog'.'b:item'.'b:title'
println mapped.'b:catalog'.'b:item'.'a:name'

// Look up namespace URIs
println nsResult.lookupNamespace('book')    // "http://example.com/book"
println nsResult.lookupNamespace('author')  // "http://example.com/author"

Complex Queries

def library = slurper.parseText('''
    <library>
        <section name="Fiction">
            <book rating="4.5" available="true">
                <title>The Great Gatsby</title>
                <author>F. Scott Fitzgerald</author>
                <year>1925</year>
            </book>
            <book rating="4.8" available="false">
                <title>To Kill a Mockingbird</title>
                <author>Harper Lee</author>
                <year>1960</year>
            </book>
        </section>
        <section name="Science">
            <book rating="4.2" available="true">
                <title>A Brief History of Time</title>
                <author>Stephen Hawking</author>
                <year>1988</year>
            </book>
        </section>
    </library>
''')

// Complex filtering with multiple conditions
def highRatedAvailableBooks = library.section.book.findAll { book ->
    book.'@rating'.toDouble() > 4.0 && 
    book.'@available'.toBoolean()
}

highRatedAvailableBooks.each { book ->
    println "${book.title} (${book.'@rating'}) - Available"
}

// Find books published after a certain year
def modernBooks = library.'**'.book.findAll { 
    it.year.toInteger() > 1950 
}

// Get all unique authors
def authors = library.'**'.author.text().unique()
println "Authors: ${authors.join(', ')}"

// Calculate average rating
def ratings = library.'**'.book.'@rating'*.toDouble()
def avgRating = ratings.sum() / ratings.size()
println "Average rating: ${avgRating}"

Performance Considerations

GPathResult uses lazy evaluation, which provides several benefits:

// Lazy evaluation - elements are only processed when accessed
def largeDoc = slurper.parseText(veryLargeXmlString)
def firstMatch = largeDoc.'**'.findAll { it.name() == 'target' }[0]
// Only processes until first match is found

// Memory efficient navigation
largeDoc.section.each { section ->
    // Process one section at a time without loading entire document into memory
    processSection(section)
}

// Avoid unnecessary iterations
def hasErrors = largeDoc.'**'.find { it.name() == 'error' }
if (hasErrors) {
    // Only searches until first error is found
    handleErrors()
}

Working with Large Documents

// Streaming-like processing with slurper
def processLargeDocument(xmlFile) {
    def slurper = new XmlSlurper()
    def doc = slurper.parse(xmlFile)
    
    // Process in chunks to manage memory
    doc.dataSection.records.record.each { record ->
        if (record.'@type' == 'important') {
            processImportantRecord(record)
        }
    }
}

// Selective loading
def loadOnlyRequired = { xmlFile ->
    def slurper = new XmlSlurper()
    def doc = slurper.parse(xmlFile)
    
    // Only load what's needed
    return doc.configuration.findAll { it.'@enabled' == 'true' }
}

Install with Tessl CLI

npx tessl i tessl/maven-org-codehaus-groovy--groovy-xml

docs

builders.md

entities.md

index.md

jaxb.md

namespaces.md

navigation.md

parsing.md

streaming.md

utilities.md

tile.json