Skip to content

Groovy Guide — Builders: Constructing Hierarchical Structures

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.

Groovy builders use closure delegation to create a DSL for constructing hierarchical structures, making XML, HTML, JSON, and GUI generation concise and readable.

What You'll Learn

  • MarkupBuilder for XML/HTML
  • JsonBuilder for JSON
  • SwingBuilder for UIs
  • Custom builders
  • Builder patterns

Why It Matters

Builders turn complex object construction into readable domain-specific code. Durga Antivirus Pro uses builders for configuration generation.

Real-World Use

Generating XML documents, creating JSON responses, building Swing UIs, and constructing test fixtures.

flowchart LR
    A["Builders"] --> B["MarkupBuilder"]
    B --> C["JsonBuilder"]
    C --> D["SwingBuilder"]
    D --> E["Custom"]
    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

MarkupBuilder

import groovy.xml.MarkupBuilder

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

xml.library() {
    book(id: "1") {
        title("Groovy in Action")
        author("Dierk Konig")
        price("49.99")
    }
    book(id: "2") {
        title("Making Java Groovy")
        author("Ken Kousen")
        price("39.99")
    }
}

println writer.toString()

MarkupBuilder for HTML

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

html.html {
    head {
        title("My Page")
        link(rel: "stylesheet", href: "style.css")
    }
    body {
        h1("Welcome")
        p(class: "intro", "This is Groovy HTML generation.")
        ul {
            li("Item 1")
            li("Item 2")
            li("Item 3")
        }
    }
}

println writer.toString()

JsonBuilder

import groovy.json.JsonBuilder

def json = new JsonBuilder()

json.users {
    user(name: "Alice", age: 30) {
        email("alice@example.com")
        roles("admin", "user")
    }
    user(name: "Bob", age: 25) {
        email("bob@example.com")
        roles("user")
    }
}

println json.toPrettyString()

StreamingJsonBuilder

import groovy.json.StreamingJsonBuilder

def writer = new StringWriter()
def json = new StreamingJsonBuilder(writer)

json {
    name "Alice"
    age 30
    address {
        city "Portland"
        state "OR"
    }
    skills "Java", "Groovy", "Python"
}

println writer.toString()

SwingBuilder

import groovy.swing.SwingBuilder
import javax.swing.*

def frame = new SwingBuilder().frame(
    title: "Groovy GUI",
    size: [400, 300],
    defaultCloseOperation: JFrame.EXIT_ON_CLOSE
) {
    panel(border: emptyBorder(10)) {
        vbox {
            label("Name:")
            textField(columns: 20)
            label("Age:")
            spinner(model: SpinnerNumberModel(30, 0, 150, 1))
            button("Submit", actionPerformed: {
                println "Clicked!"
            })
        }
    }
}

frame.visible = true

Custom Builder

class ConfigBuilder {
    def properties = [:]

    def propertyMissing(String name, value) {
        properties[name] = value
    }

    def server(Closure cl) {
        def builder = new ConfigBuilder()
        builder.properties.server = [:]
        cl.delegate = builder
        cl.resolveStrategy = Closure.DELEGATE_FIRST
        cl()
        properties.server = builder.properties
    }

    def build() { properties }
}

def config = new ConfigBuilder()
config.build {
    server {
        host "localhost"
        port 8080
        ssl false
    }
    database {
        url "jdbc:postgresql://localhost/db"
        username "admin"
    }
}

println config

Common Mistakes

1. Builder method name collisions

When builder elements have the same name as local methods, naming conflicts occur. Use explicit closure syntax.

2. Forgetting to set resolve Strategy

Builder closures need DELEGATE_FIRST or DELEGATE_ONLY strategy to work correctly.

3. XML special characters

MarkupBuilder handles escaping automatically. Don't pre-escape content.

4. Builder state reuse

Builders maintain state. Create a new builder instance for each document.

5. Nested builder confusion

Mix builder types carefully. Each builder has its own delegate context.

Practice Questions

1. What is a Groovy builder? A DSL using closure delegation to construct hierarchical structures like XML, JSON, or UIs.

2. How does closure delegation work in builders? Method calls inside the closure are delegated to the builder, which interprets them as element or attribute creation.

3. What is MarkupBuilder used for? Generating XML and HTML markup using Groovy closure syntax with automatic escaping.

Challenge: Build a custom builder for a simple configuration DSL.

FAQ

{{< faq question="Are builders thread-safe?" >} No. Builders maintain internal state. Create a new builder per thread. {{< /faq >}}

{{< faq question="Can builders create text nodes?" >} Yes, with mkp.yield: mkp.yield("text content") in MarkupBuilder. {{< /faq >}}

{{< faq question="What is the difference between JsonBuilder and StreamingJsonBuilder?" >} JsonBuilder creates an in-memory structure. StreamingJsonBuilder writes directly to a writer for large documents. {{< /faq >}}

{{< faq question="Can I nest different builder types?" >} Yes, but manage delegate context carefully. Each builder needs its own closure scope. {{< /faq >}}

{{< faq question="What is nodeBuilder?" >} NodeBuilder creates a tree of Node objects for in-memory hierarchical data without Serialization. {{< /faq >}}

Mini Project

Generate an HTML report with MarkupBuilder:

def generateReport(rows) {
    def writer = new StringWriter()
    def html = new MarkupBuilder(writer)

    html.html {
        head { title("Report") }
        body {
            h1("Data Report")
            table(border: "1") {
                tr {
                    th("Name")
                    th("Value")
                    th("Status")
                }
                rows.each { row ->
                    tr {
                        td(row.name)
                        td(row.value.toString())
                        td(row.status)
                    }
                }
            }
        }
    }
    return writer.toString()
}

def data = [
    [name: "CPU", value: 45, status: "OK"],
    [name: "Memory", value: 80, status: "Warning"],
    [name: "Disk", value: 92, status: "Critical"],
]

println generateReport(data)

What's Next

Now that you understand builders, explore XML processing in Groovy.

Topic Description Link
Groovy XML XML processing {{< ref "10-xml" >}}
Groovy JSON JSON processing {{< ref "11-json" >}}

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro