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.
Table of Contents
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.
| Class | Purpose | Best For |
|---|---|---|
JsonOutput | Static utility – converts objects to JSON strings | Quick conversions from maps, lists, POJOs |
JsonBuilder | DSL-based builder – construct JSON with a closure | Building complex JSON structures in-memory |
StreamingJsonBuilder | Streaming builder – writes JSON directly to a Writer | Large JSON output, HTTP responses, file writes |
Key Points:
- All three are in
groovy.json– no external dependencies needed JsonOutputis a static utility class – no instantiation requiredJsonBuilderbuilds the entire JSON in memory before producing a stringStreamingJsonBuilderwrites directly to aWriter– 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 construction –
JsonBuilderlets 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
JsonBuilderwhen you need to construct complex JSON structures - Use
StreamingJsonBuilderfor large output or when writing to files/streams - Use
JsonGenerator.Optionsto customize date formats and exclude fields - Use
toPrettyString()onJsonBuilderfor 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
JsonBuilderfor very large data sets – useStreamingJsonBuilderinstead - 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 callJsonOutput.prettyPrint()formats a JSON string – takes a string, not an objectJsonBuilderuses a DSL with closures for nested objects and lists for arraysStreamingJsonBuilderwrites directly to a Writer for memory-efficient outputJsonGenerator.Optionscustomizes 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.
Related Posts
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

No comment