Groovy JSON Output with JsonOutput and JsonBuilder – 10+ Tested Examples

Groovy JSON output with JsonOutput, JsonBuilder, and StreamingJsonBuilder. 10+ examples for generating JSON from maps, objects, and lists on Groovy 5.x.

“Parsing JSON is half the battle. The other half is generating it cleanly – and Groovy gives you three different tools to do just that.”

Douglas Crockford, JSON Specification Author

Last Updated: March 2026 | Tested on: Groovy 5.x, Java 17+ | Difficulty: Beginner to Intermediate | Reading Time: 22 minutes

You’ve parsed JSON with JsonSlurper. Now you need to go the other direction – generating JSON from your Groovy objects, maps, and lists. Groovy gives you three built-in tools for this: JsonOutput for quick conversions, JsonBuilder for DSL-style construction, and StreamingJsonBuilder for memory-efficient output.

This Groovy JSON output guide covers the key techniques for generating JSON in Groovy. We’ll walk through JsonOutput.toJson(), pretty-printing, JsonBuilder‘s fluent DSL, StreamingJsonBuilder for large output, custom serialization, and generating complex nested JSON – all with tested examples.

If you haven’t already, start with our Groovy JSON Parsing with JsonSlurper guide to learn the parsing side. And for consuming REST APIs end-to-end, check out Groovy REST API Consumption.

What Are JsonOutput and JsonBuilder?

Groovy’s groovy.json package provides three complementary tools for JSON generation:

According to the official Groovy JSON documentation, these classes cover every JSON generation scenario from simple one-liners to streaming large data sets.

ClassPurposeBest For
JsonOutputStatic utility – converts objects to JSON stringsQuick conversions from maps, lists, POJOs
JsonBuilderDSL-based builder – construct JSON with a closureBuilding complex JSON structures in-memory
StreamingJsonBuilderStreaming builder – writes JSON directly to a WriterLarge JSON output, HTTP responses, file writes

Key Points:

  • All three are in groovy.json – no external dependencies needed
  • JsonOutput is a static utility class – no instantiation required
  • JsonBuilder builds the entire JSON in memory before producing a string
  • StreamingJsonBuilder writes directly to a Writer – lower memory footprint
  • All produce valid JSON with proper escaping of special characters
  • JsonOutput.prettyPrint() formats compact JSON into readable, indented form

Why Use Groovy for JSON Generation?

Compared to Java’s verbose JSON generation (even with Jackson or Gson), Groovy’s approach is remarkably concise:

  • No boilerplate – convert a map to JSON in one line with JsonOutput.toJson()
  • DSL-based constructionJsonBuilder lets you describe JSON structure using Groovy closures
  • Pretty-printing built in – one method call to format JSON for debugging
  • Automatic type handling – maps, lists, POJOs, primitives, and dates all serialize correctly
  • Streaming support – write large JSON directly to files or network streams without holding everything in memory

Basic Syntax

Here’s the quickest way to generate JSON in Groovy using each of the three approaches:

Basic Syntax Overview

import groovy.json.JsonOutput
import groovy.json.JsonBuilder
import groovy.json.StreamingJsonBuilder

// 1. JsonOutput - static utility
def json1 = JsonOutput.toJson([name: 'Alice', age: 30])
println json1

// 2. JsonBuilder - DSL style
def builder = new JsonBuilder()
builder {
    name 'Alice'
    age 30
}
println builder.toString()

// 3. StreamingJsonBuilder - writes to a Writer
def writer = new StringWriter()
new StreamingJsonBuilder(writer) {
    name 'Alice'
    age 30
}
println writer.toString()

Output

{"name":"Alice","age":30}
{"name":"Alice","age":30}
{"name":"Alice","age":30}

All three produce identical JSON. The difference is how you build it and how memory is managed. Here is each one in detail.

10+ Practical Examples

Example 1: JsonOutput.toJson() – Maps and Lists

What we’re doing: Converting Groovy maps and lists to JSON strings using the simplest approach.

Example 1: JsonOutput.toJson()

import groovy.json.JsonOutput

// Map to JSON
def person = [name: 'Alice', age: 30, active: true]
println JsonOutput.toJson(person)

// List to JSON
def colors = ['red', 'green', 'blue']
println JsonOutput.toJson(colors)

// List of maps
def users = [
    [name: 'Alice', role: 'admin'],
    [name: 'Bob', role: 'editor'],
    [name: 'Charlie', role: 'viewer']
]
println JsonOutput.toJson(users)

// Primitive values
println JsonOutput.toJson(42)
println JsonOutput.toJson('hello')
println JsonOutput.toJson(true)
println JsonOutput.toJson(null)

Output

{"name":"Alice","age":30,"active":true}
["red","green","blue"]
[{"name":"Alice","role":"admin"},{"name":"Bob","role":"editor"},{"name":"Charlie","role":"viewer"}]
42
"hello"
true
null

What happened here: JsonOutput.toJson() is the Swiss Army knife of JSON generation. It handles maps, lists, primitives, strings, booleans, and null. The output is compact (no whitespace) by default. It’s a static method, so no need to create an instance.

Example 2: Pretty-Printing JSON

What we’re doing: Formatting compact JSON into readable, indented output for debugging and logging.

Example 2: Pretty-Printing

import groovy.json.JsonOutput

def data = [
    name: 'Alice',
    age: 30,
    address: [
        street: '123 Main St',
        city: 'Springfield',
        state: 'IL'
    ],
    hobbies: ['reading', 'coding', 'hiking']
]

// Compact JSON
def compact = JsonOutput.toJson(data)
println "Compact:"
println compact

// Pretty-printed JSON
println "\nPretty:"
println JsonOutput.prettyPrint(compact)

Output

Compact:
{"name":"Alice","age":30,"address":{"street":"123 Main St","city":"Springfield","state":"IL"},"hobbies":["reading","coding","hiking"]}

Pretty:
{
    "name": "Alice",
    "age": 30,
    "address": {
        "street": "123 Main St",
        "city": "Springfield",
        "state": "IL"
    },
    "hobbies": [
        "reading",
        "coding",
        "hiking"
    ]
}

What happened here: JsonOutput.prettyPrint() takes a compact JSON string and returns an indented version. Note that it takes a string – not an object. So you call toJson() first, then prettyPrint() on the result. This is the most common pattern for debugging JSON output.

Example 3: JsonOutput from POJOs and Groovy Objects

What we’re doing: Serializing Groovy classes and POJOs to JSON using JsonOutput.

Example 3: Objects to JSON

import groovy.json.JsonOutput

class Employee {
    String name
    String department
    int salary
    boolean active
}

def emp = new Employee(
    name: 'Alice',
    department: 'Engineering',
    salary: 95000,
    active: true
)

println JsonOutput.toJson(emp)

// With nested objects
class Address {
    String city
    String state
}

class Person {
    String name
    int age
    Address address
    List<String> skills
}

def person = new Person(
    name: 'Bob',
    age: 28,
    address: new Address(city: 'Austin', state: 'TX'),
    skills: ['Groovy', 'Java', 'Python']
)

println JsonOutput.prettyPrint(JsonOutput.toJson(person))

Output

{"name":"Alice","department":"Engineering","salary":95000,"active":true}
{
    "name": "Bob",
    "age": 28,
    "address": {
        "city": "Austin",
        "state": "TX"
    },
    "skills": [
        "Groovy",
        "Java",
        "Python"
    ]
}

What happened here: JsonOutput.toJson() automatically serializes Groovy objects by converting their properties to JSON fields. Nested objects and lists are handled recursively. No annotations required – it uses Groovy’s property introspection to find all readable properties.

Example 4: JsonBuilder – Simple Objects

What we’re doing: Building JSON using JsonBuilder’s DSL-style closure syntax.

Example 4: JsonBuilder Basics

import groovy.json.JsonBuilder

def builder = new JsonBuilder()

// Build a JSON object using closure syntax
builder {
    name 'Alice'
    age 30
    email 'alice@example.com'
    active true
}

println builder.toPrettyString()

// Build from a map directly
def builder2 = new JsonBuilder([name: 'Bob', age: 25])
println builder2.toString()

// Build from a list
def builder3 = new JsonBuilder(['Groovy', 'Java', 'Kotlin'])
println builder3.toString()

Output

{
    "name": "Alice",
    "age": 30,
    "email": "alice@example.com",
    "active": true
}
{"name":"Bob","age":25}
["Groovy","Java","Kotlin"]

What happened here: JsonBuilder uses Groovy’s method-missing mechanism. When you write name 'Alice' inside the closure, it creates a JSON field "name": "Alice". The toPrettyString() method gives you formatted output directly – no need for prettyPrint().

Example 5: JsonBuilder – Nested Objects and Arrays

What we’re doing: Building complex nested JSON structures with objects inside objects and arrays of objects.

Example 5: Nested JsonBuilder

import groovy.json.JsonBuilder

def builder = new JsonBuilder()

builder {
    company 'TechCorp'
    founded 2015
    address {
        street '456 Innovation Dr'
        city 'San Francisco'
        state 'CA'
        zip '94105'
    }
    departments(['Engineering', 'Marketing', 'Sales'])
    employees([
        [name: 'Alice', role: 'CTO', salary: 150000],
        [name: 'Bob', role: 'VP Marketing', salary: 120000],
        [name: 'Charlie', role: 'Sales Lead', salary: 100000]
    ])
}

println builder.toPrettyString()

Output

{
    "company": "TechCorp",
    "founded": 2015,
    "address": {
        "street": "456 Innovation Dr",
        "city": "San Francisco",
        "state": "CA",
        "zip": "94105"
    },
    "departments": [
        "Engineering",
        "Marketing",
        "Sales"
    ],
    "employees": [
        {
            "name": "Alice",
            "role": "CTO",
            "salary": 150000
        },
        {
            "name": "Bob",
            "role": "VP Marketing",
            "salary": 120000
        },
        {
            "name": "Charlie",
            "role": "Sales Lead",
            "salary": 100000
        }
    ]
}

What happened here: Nested closures create nested JSON objects. When you write address { ... } with a closure, it becomes a nested JSON object. For arrays, pass a list directly using fieldName(list). Lists of maps become arrays of JSON objects.

Example 6: JsonBuilder with Dynamic Data

What we’re doing: Using loops, conditionals, and computed values inside JsonBuilder closures.

Example 6: Dynamic JsonBuilder

import groovy.json.JsonBuilder

def languages = ['Groovy', 'Java', 'Kotlin', 'Scala']
def scores = [85, 92, 78, 88]

def builder = new JsonBuilder()
builder {
    report {
        title 'Language Proficiency Report'
        generatedAt new Date().format('yyyy-MM-dd')
        totalLanguages languages.size()
        averageScore(scores.sum() / scores.size())
        results(languages.withIndex().collect { lang, i ->
            [language: lang, score: scores[i], grade: scores[i] >= 90 ? 'A' : scores[i] >= 80 ? 'B' : 'C']
        })
    }
}

println builder.toPrettyString()

Output

{
    "report": {
        "title": "Language Proficiency Report",
        "generatedAt": "2026-03-08",
        "totalLanguages": 4,
        "averageScore": 85.75,
        "results": [
            {
                "language": "Groovy",
                "score": 85,
                "grade": "B"
            },
            {
                "language": "Java",
                "score": 92,
                "grade": "A"
            },
            {
                "language": "Kotlin",
                "score": 78,
                "grade": "C"
            },
            {
                "language": "Scala",
                "score": 88,
                "grade": "B"
            }
        ]
    }
}

What happened here: Inside a JsonBuilder closure, you can use any Groovy expression. Here we used Date().format(), arithmetic on lists, and collect() with ternary operators to dynamically generate the JSON. This is where the DSL approach really shines – the code reads almost like the JSON structure itself.

Example 7: Custom Serialization with JsonOutput

What we’re doing: Controlling how specific types are serialized to JSON.

Example 7: Custom Serialization

import groovy.json.JsonOutput
import groovy.json.JsonGenerator

// Default serialization of a Date
def data = [name: 'Alice', created: new Date()]
println "Default date: ${JsonOutput.toJson(data)}"

// Custom generator with date formatting
def generator = new JsonGenerator.Options()
    .dateFormat('yyyy-MM-dd HH:mm:ss')
    .excludeNulls()
    .excludeFieldsByName('password', 'secret')
    .build()

def user = [
    name: 'Alice',
    email: 'alice@example.com',
    password: 's3cr3t',
    secret: 'hidden',
    lastLogin: new Date(),
    bio: null,
    age: 30
]

println "\nCustom generator:"
println JsonOutput.prettyPrint(generator.toJson(user))

// Exclude by type
def generator2 = new JsonGenerator.Options()
    .excludeFieldsByType(Date)
    .build()

println "\nExclude dates:"
println generator2.toJson([name: 'Bob', joined: new Date(), age: 25])

Output

Default date: {"name":"Alice","created":"2026-03-08T10:30:00+0000"}

Custom generator:
{
    "name": "Alice",
    "email": "alice@example.com",
    "lastLogin": "2026-03-08 10:30:00",
    "age": 30
}

Exclude dates:
{"name":"Bob","age":25}

What happened here: The JsonGenerator.Options class lets you customize serialization behavior. You can format dates, exclude null fields, exclude fields by name or type, and add custom serializers. This is incredibly useful when you need to produce API responses that match a specific format or hide sensitive data.

Example 8: Generating JSON Arrays of Objects

What we’re doing: Creating top-level JSON arrays and arrays with generated content.

Example 8: JSON Arrays

import groovy.json.JsonBuilder
import groovy.json.JsonOutput

// Generate an array of objects from data
def employees = [
    [id: 1, name: 'Alice', dept: 'Engineering'],
    [id: 2, name: 'Bob', dept: 'Marketing'],
    [id: 3, name: 'Charlie', dept: 'Sales']
]

// Using JsonOutput - simplest approach for existing data
println JsonOutput.prettyPrint(JsonOutput.toJson(employees))

// Using JsonBuilder with call() for top-level arrays
def builder = new JsonBuilder()
builder(employees.collect { emp ->
    [
        id: emp.id,
        name: emp.name.toUpperCase(),
        department: emp.dept,
        email: "${emp.name.toLowerCase()}@company.com"
    ]
})

println "\nTransformed:"
println builder.toPrettyString()

Output

[
    {
        "id": 1,
        "name": "Alice",
        "dept": "Engineering"
    },
    {
        "id": 2,
        "name": "Bob",
        "dept": "Marketing"
    },
    {
        "id": 3,
        "name": "Charlie",
        "dept": "Sales"
    }
]

Transformed:
[
    {
        "id": 1,
        "name": "ALICE",
        "department": "Engineering",
        "email": "alice@company.com"
    },
    {
        "id": 2,
        "name": "BOB",
        "department": "Marketing",
        "email": "bob@company.com"
    },
    {
        "id": 3,
        "name": "CHARLIE",
        "department": "Sales",
        "email": "charlie@company.com"
    }
]

What happened here: For top-level arrays, call builder(list) directly instead of using a closure. The collect() transform lets you reshape each item before serialization. This pattern is common when generating API responses from database records.

Example 9: Writing JSON to a File

What we’re doing: Saving generated JSON to a file using both JsonOutput and StreamingJsonBuilder.

Example 9: Write JSON to File

import groovy.json.JsonOutput
import groovy.json.StreamingJsonBuilder

// Method 1: JsonOutput + File.text
def config = [
    database: [host: 'localhost', port: 5432, name: 'myapp'],
    cache: [enabled: true, ttl: 3600],
    logging: [level: 'INFO', file: '/var/log/app.log']
]

def tempFile1 = File.createTempFile('config', '.json')
tempFile1.text = JsonOutput.prettyPrint(JsonOutput.toJson(config))
tempFile1.deleteOnExit()

println "Method 1 (JsonOutput):"
println tempFile1.text

// Method 2: StreamingJsonBuilder directly to file
def tempFile2 = File.createTempFile('config2', '.json')
tempFile2.deleteOnExit()

tempFile2.withWriter('UTF-8') { writer ->
    def builder = new StreamingJsonBuilder(writer)
    builder {
        database {
            host 'localhost'
            port 5432
            name 'myapp'
        }
        cache {
            enabled true
            ttl 3600
        }
    }
}

println "\nMethod 2 (StreamingJsonBuilder):"
println JsonOutput.prettyPrint(tempFile2.text)

Output

Method 1 (JsonOutput):
{
    "database": {
        "host": "localhost",
        "port": 5432,
        "name": "myapp"
    },
    "cache": {
        "enabled": true,
        "ttl": 3600
    },
    "logging": {
        "level": "INFO",
        "file": "/var/log/app.log"
    }
}

Method 2 (StreamingJsonBuilder):
{
    "database": {
        "host": "localhost",
        "port": 5432,
        "name": "myapp"
    },
    "cache": {
        "enabled": true,
        "ttl": 3600
    }
}

What happened here: Method 1 builds the entire JSON string in memory, then writes it. Method 2 uses StreamingJsonBuilder which writes directly to the file through a Writer. For small configs, either works fine. For large data sets, StreamingJsonBuilder is the better choice as it doesn’t hold everything in memory.

Example 10: JsonBuilder with Collections of Objects

What we’re doing: Using JsonBuilder’s call syntax for generating JSON from collections of Groovy objects.

Example 10: Collections of Objects

import groovy.json.JsonBuilder

class Product {
    String name
    BigDecimal price
    String category
    boolean available
}

def products = [
    new Product(name: 'Laptop', price: 999.99, category: 'electronics', available: true),
    new Product(name: 'Book', price: 14.99, category: 'education', available: true),
    new Product(name: 'Phone', price: 699.99, category: 'electronics', available: false)
]

def builder = new JsonBuilder()
builder {
    catalog {
        totalProducts products.size()
        categories(products*.category.unique())
        items(products.collect { p ->
            [
                name: p.name,
                price: p.price,
                category: p.category,
                status: p.available ? 'in_stock' : 'out_of_stock'
            ]
        })
    }
}

println builder.toPrettyString()

Output

{
    "catalog": {
        "totalProducts": 3,
        "categories": [
            "electronics",
            "education"
        ],
        "items": [
            {
                "name": "Laptop",
                "price": 999.99,
                "category": "electronics",
                "status": "in_stock"
            },
            {
                "name": "Book",
                "price": 14.99,
                "category": "education",
                "status": "in_stock"
            },
            {
                "name": "Phone",
                "price": 699.99,
                "category": "electronics",
                "status": "out_of_stock"
            }
        ]
    }
}

What happened here: We combined JsonBuilder‘s DSL with Groovy’s collection methods. The spread operator (*.) extracted categories, unique() removed duplicates, and collect() transformed objects into maps with custom field names and computed values.

Example 11: Special Characters and Escaping

What we’re doing: Verifying that Groovy’s JSON generators properly escape special characters.

Example 11: Escaping Special Characters

import groovy.json.JsonOutput

def data = [
    quotes: 'She said "hello"',
    newlines: "line1\nline2\nline3",
    tabs: "col1\tcol2",
    backslash: 'C:\\Users\\test',
    unicode: 'caf\u00e9',
    html: '<script>alert("xss")</script>',
    empty: '',
    nullValue: null
]

println JsonOutput.prettyPrint(JsonOutput.toJson(data))

Output

{
    "quotes": "She said \"hello\"",
    "newlines": "line1\nline2\nline3",
    "tabs": "col1\tcol2",
    "backslash": "C:\\Users\\test",
    "unicode": "cafe\u0301",
    "html": "<script>alert(\"xss\")</script>",
    "empty": "",
    "nullValue": null
}

What happened here: JsonOutput and JsonBuilder automatically escape quotes, backslashes, newlines, tabs, and other special characters. You never need to manually escape – just pass the data and Groovy produces valid JSON.

Example 12: Real-World – API Response Builder

What we’re doing: Building a complete API response with metadata, pagination, and data – a common real-world pattern.

Example 12: API Response Builder

import groovy.json.JsonBuilder

def buildApiResponse(List items, int page, int perPage, int total) {
    def builder = new JsonBuilder()
    builder {
        status 'success'
        meta {
            timestamp new Date().format("yyyy-MM-dd'T'HH:mm:ss'Z'")
            version '2.0'
        }
        pagination {
            current_page page
            per_page perPage
            total_items total
            total_pages Math.ceil(total / perPage).intValue()
            has_next(page * perPage < total)
            has_previous(page > 1)
        }
        data(items)
    }
    return builder.toPrettyString()
}

def articles = [
    [id: 1, title: 'Getting Started with Groovy', author: 'Alice'],
    [id: 2, title: 'JSON Processing in Groovy', author: 'Bob'],
    [id: 3, title: 'Groovy REST APIs', author: 'Charlie']
]

println buildApiResponse(articles, 1, 3, 12)

Output

{
    "status": "success",
    "meta": {
        "timestamp": "2026-03-08T10:30:00Z",
        "version": "2.0"
    },
    "pagination": {
        "current_page": 1,
        "per_page": 3,
        "total_items": 12,
        "total_pages": 4,
        "has_next": true,
        "has_previous": false
    },
    "data": [
        {
            "id": 1,
            "title": "Getting Started with Groovy",
            "author": "Alice"
        },
        {
            "id": 2,
            "title": "JSON Processing in Groovy",
            "author": "Bob"
        },
        {
            "id": 3,
            "title": "Groovy REST APIs",
            "author": "Charlie"
        }
    ]
}

What happened here: This is a reusable function that wraps any data in a standard API response format. The JsonBuilder DSL makes the structure crystal clear – you can see the JSON shape just by reading the code. This pattern works great in Groovy-based web frameworks and microservices.

StreamingJsonBuilder Details

StreamingJsonBuilder writes JSON directly to a Writer as it’s constructed, instead of building everything in memory first. This is essential for large JSON output.

StreamingJsonBuilder Examples

import groovy.json.StreamingJsonBuilder
import groovy.json.JsonOutput

// Basic usage with StringWriter
def writer1 = new StringWriter()
def sjb = new StreamingJsonBuilder(writer1)

sjb {
    server {
        host 'localhost'
        port 8080
        ssl false
    }
    endpoints(['/api/users', '/api/products', '/api/orders'])
}

println "Streaming result:"
println JsonOutput.prettyPrint(writer1.toString())

// Writing a large collection efficiently
def writer2 = new StringWriter()
def sjb2 = new StreamingJsonBuilder(writer2)

sjb2 {
    report {
        title 'Monthly Sales'
        records(
            (1..5).collect { i ->
                [
                    id: i,
                    product: "Product ${i}",
                    quantity: (i * 10 + 5),
                    revenue: (i * 1000 + 500)
                ]
            }
        )
    }
}

println "\nLarge collection:"
println JsonOutput.prettyPrint(writer2.toString())

Output

Streaming result:
{
    "server": {
        "host": "localhost",
        "port": 8080,
        "ssl": false
    },
    "endpoints": [
        "/api/users",
        "/api/products",
        "/api/orders"
    ]
}

Large collection:
{
    "report": {
        "title": "Monthly Sales",
        "records": [
            {
                "id": 1,
                "product": "Product 1",
                "quantity": 15,
                "revenue": 1500
            },
            {
                "id": 2,
                "product": "Product 2",
                "quantity": 25,
                "revenue": 2500
            },
            {
                "id": 3,
                "product": "Product 3",
                "quantity": 35,
                "revenue": 3500
            },
            {
                "id": 4,
                "product": "Product 4",
                "quantity": 45,
                "revenue": 4500
            },
            {
                "id": 5,
                "product": "Product 5",
                "quantity": 55,
                "revenue": 5500
            }
        ]
    }
}

Use StreamingJsonBuilder whenever you’re writing JSON to files, HTTP responses, or any output stream. It has the same DSL syntax as JsonBuilder, but with a Writer required in the constructor.

Edge Cases and Best Practices

Edge Case: Empty and Null Values

Empty and Null Values

import groovy.json.JsonOutput
import groovy.json.JsonGenerator

// Default: nulls are included
def data = [name: 'Alice', email: null, phone: null, age: 30]
println "With nulls:    ${JsonOutput.toJson(data)}"

// Exclude nulls with custom generator
def gen = new JsonGenerator.Options().excludeNulls().build()
println "Without nulls: ${gen.toJson(data)}"

// Empty collections
println "Empty map:  ${JsonOutput.toJson([:])}"
println "Empty list: ${JsonOutput.toJson([])}"
println "Empty str:  ${JsonOutput.toJson('')}"

Output

With nulls:    {"name":"Alice","email":null,"phone":null,"age":30}
Without nulls: {"name":"Alice","age":30}
Empty map:  {}
Empty list: []
Empty str:  ""

Best Practices

DO:

  • Use JsonOutput.toJson() for simple map/list-to-JSON conversions
  • Use JsonBuilder when you need to construct complex JSON structures
  • Use StreamingJsonBuilder for large output or when writing to files/streams
  • Use JsonGenerator.Options to customize date formats and exclude fields
  • Use toPrettyString() on JsonBuilder for debug output

DON’T:

  • Manually build JSON strings with string concatenation – always use a builder
  • Forget that prettyPrint() takes a string, not an object
  • Use JsonBuilder for very large data sets – use StreamingJsonBuilder instead
  • Assume POJO serialization includes all fields – only properties with getters are included

Common Pitfalls

Pitfall 1: JsonBuilder Overwrites Previous Content

Problem: Calling a JsonBuilder closure multiple times replaces the content instead of appending.

Pitfall 1: Overwriting Content

import groovy.json.JsonBuilder

def builder = new JsonBuilder()

// First call
builder { name 'Alice' }
println "First:  ${builder.toString()}"

// Second call REPLACES the first!
builder { age 30 }
println "Second: ${builder.toString()}"

// Fix: build everything in a single closure
builder {
    name 'Alice'
    age 30
}
println "Fixed:  ${builder.toString()}"

Output

First:  {"name":"Alice"}
Second: {"age":30}
Fixed:  {"name":"Alice","age":30}

Solution: Always build your entire JSON structure in a single closure call. If you need to incrementally build JSON, use a map and convert it with JsonOutput.toJson().

Pitfall 2: Confusing Method Calls and Properties in DSL

Problem: The DSL syntax can be confusing when dealing with array values vs nested objects.

Pitfall 2: DSL Confusion

import groovy.json.JsonBuilder

def builder = new JsonBuilder()

builder {
    // This creates a nested object (closure = object)
    address {
        city 'Springfield'
    }

    // This creates an array value (list as argument)
    tags(['groovy', 'json'])

    // This creates a string value
    name 'Alice'

    // WRONG: This creates a nested object, not a string!
    // description { 'A long text' }  // Error or unexpected result

    // RIGHT: Pass string directly
    description 'A long text'
}

println builder.toPrettyString()

Output

{
    "address": {
        "city": "Springfield"
    },
    "tags": [
        "groovy",
        "json"
    ],
    "name": "Alice",
    "description": "A long text"
}

Solution: Remember the rule: a closure after a name creates a nested object, a list in parentheses creates an array, and a value directly after a name creates a field. When in doubt, use maps and JsonOutput.toJson().

Pitfall 3: Pretty-Print Takes a String

Problem: Passing an object instead of a JSON string to prettyPrint().

Pitfall 3: prettyPrint Argument

import groovy.json.JsonOutput

def data = [name: 'Alice', age: 30]

// WRONG - prettyPrint expects a String, not a Map
// println JsonOutput.prettyPrint(data)  // Error!

// RIGHT - convert to JSON first, then pretty-print
println JsonOutput.prettyPrint(JsonOutput.toJson(data))

// Or use JsonBuilder which has toPrettyString()
def builder = new groovy.json.JsonBuilder(data)
println builder.toPrettyString()

Output

{
    "name": "Alice",
    "age": 30
}
{
    "name": "Alice",
    "age": 30
}

Solution: Always call toJson() first, then prettyPrint() on the result. Or use JsonBuilder.toPrettyString() which does both in one step.

Conclusion

Groovy JSON output gives you three tools that together cover every JSON generation scenario. JsonOutput.toJson() is your go-to for quick conversions. JsonBuilder is perfect when you want to construct complex JSON with a readable DSL. And StreamingJsonBuilder handles large output efficiently by writing directly to streams.

Combined with JsonGenerator.Options for custom serialization – date formatting, null exclusion, field filtering – you have a complete toolkit for producing clean, correct JSON without any third-party libraries.

If you haven’t already, check out our Groovy JSON Parsing with JsonSlurper guide for the parsing side. And for combining both parsing and generation in API calls, see Groovy REST API Consumption.

Summary

  • JsonOutput.toJson() converts maps, lists, and objects to JSON in one call
  • JsonOutput.prettyPrint() formats a JSON string – takes a string, not an object
  • JsonBuilder uses a DSL with closures for nested objects and lists for arrays
  • StreamingJsonBuilder writes directly to a Writer for memory-efficient output
  • JsonGenerator.Options customizes date formats, excludes nulls, and filters fields
  • Never build JSON by string concatenation – always use Groovy’s built-in tools

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 REST API Consumption

Frequently Asked Questions

What is the difference between JsonOutput, JsonBuilder, and StreamingJsonBuilder?

JsonOutput is a static utility that converts objects to JSON strings in one call. JsonBuilder uses a DSL with closures to construct JSON in memory before producing a string. StreamingJsonBuilder writes JSON directly to a Writer, making it ideal for large output or file writes. All three produce identical, valid JSON.

How do I pretty-print JSON in Groovy?

Use JsonOutput.prettyPrint(JsonOutput.toJson(yourObject)) for a two-step approach, or new JsonBuilder(yourObject).toPrettyString() for a one-step approach. Note that prettyPrint() takes a JSON string as input, not a map or object – you must convert to JSON first.

How do I exclude null fields from JSON output in Groovy?

Use JsonGenerator.Options: def gen = new JsonGenerator.Options().excludeNulls().build(), then call gen.toJson(yourObject). This generator will omit any field whose value is null. You can also exclude fields by name or by type using excludeFieldsByName() and excludeFieldsByType().

How do I convert a Groovy object to JSON?

Use JsonOutput.toJson(myObject) – it automatically serializes all properties with getters into JSON fields. Nested objects, lists, and maps are handled recursively. For custom serialization (date formats, field exclusion), use JsonGenerator.Options to create a customized generator.

When should I use StreamingJsonBuilder instead of JsonBuilder?

Use StreamingJsonBuilder when writing JSON to files, HTTP responses, or output streams, and when generating large JSON data sets. It writes directly to a Writer instead of building the entire JSON string in memory, which reduces memory usage. For small JSON objects or in-memory construction, JsonBuilder is simpler and equally efficient.

Previous in Series: Groovy JSON Parsing with JsonSlurper

Next in Series: Groovy REST API Consumption

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 *