Groovy JSON Parsing with JsonSlurper – 10+ Tested Examples

Groovy JSON parsing with JsonSlurper. 10+ examples covering parseText, parse URL, nested fields, arrays, lazy parsing, and error handling on Groovy 5.x.

“JSON is the duct tape of the internet. Groovy’s JsonSlurper is the scissors that makes it easy to cut through.”

Douglas Crockford, JavaScript: The Good Parts

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

If you work with web APIs, configuration files, or any modern data exchange format, you work with JSON. And if you’re writing Groovy, you have one of the cleanest JSON parsing experiences available in any JVM language – thanks to JsonSlurper.

This Groovy JSON parsing guide walks you through what you need for working with JsonSlurper. We’ll cover parsing strings, files, and URLs, accessing nested fields, handling arrays, type coercion, lazy vs non-lazy parsing, and reliable error handling – all with tested examples and real output.

Once you’ve mastered parsing, head over to our Groovy JSON Output with JsonBuilder guide to learn how to generate JSON. And if you want to consume REST APIs end-to-end, check out Groovy REST API Consumption.

What Is JsonSlurper?

groovy.json.JsonSlurper is Groovy’s built-in JSON parser. It takes a JSON string (or stream, or URL) and converts it into native Groovy data structures – maps for JSON objects and lists for JSON arrays. No third-party libraries needed. No annotations. No POJOs. Just parse and go.

According to the official Groovy JSON documentation, JsonSlurper parses text or reader content into Groovy data structures such as maps, lists, and primitive types like Integer, Long, BigDecimal, and String.

Key Points:

  • Part of the groovy.json package – no external dependencies
  • Parses JSON objects into Map instances and JSON arrays into List instances
  • Supports multiple parser types: INDEX_OVERLAY (default), LAX, CHARACTER_SOURCE, and CHAR_BUFFER
  • Handles JSON numbers as Integer, Long, or BigDecimal depending on size
  • JSON booleans become Groovy Boolean values and null becomes Groovy null
  • Thread-safe – you can reuse a single instance across threads

Why Use JsonSlurper for JSON Parsing?

You might wonder why you’d pick JsonSlurper over Jackson, Gson, or other popular JSON libraries. Here’s why:

  • Zero setup – it ships with Groovy, no Gradle or Maven dependency needed
  • Dynamic access – parsed JSON becomes maps and lists, so you use dot notation and array indexing directly
  • No POJOs required – you don’t need to define classes that mirror the JSON structure
  • Groovy-native types – numbers, booleans, and strings map directly to Groovy types
  • Multiple parser modes – pick the right parser for your performance and correctness needs
  • Great for scripting – perfect for quick scripts, CI/CD pipelines, and Jenkinsfiles

That said, if you need schema validation, custom deserializers, or streaming parsing for very large files, Jackson is still a solid choice. But for 90% of Groovy JSON work, JsonSlurper is all you need.

Basic Syntax

The basic pattern for Groovy JSON parsing is always the same: create a JsonSlurper instance, call a parse method, and work with the result.

JsonSlurper Basic Syntax

import groovy.json.JsonSlurper

def slurper = new JsonSlurper()

// Parse a JSON string
def result = slurper.parseText('{"name": "Alice", "age": 30}')

// Access fields with dot notation
println result.name  // Alice
println result.age   // 30

The main parse methods available are:

MethodInput SourceUse Case
parseText(String)JSON stringInline JSON, API responses stored as strings
parse(Reader)ReaderFiles, streams
parse(InputStream)Input streamHTTP connections, resources
parse(File)File objectLocal JSON files
parse(URL)URL objectRemote JSON endpoints
parse(byte[])Byte arrayBinary data, network buffers

10+ Practical Examples

Example 1: Parse a Simple JSON Object

What we’re doing: Parsing a basic JSON object string and accessing its properties using dot notation.

Example 1: Simple JSON Object

import groovy.json.JsonSlurper

def slurper = new JsonSlurper()
def json = slurper.parseText('''
{
    "name": "Alice",
    "age": 30,
    "active": true,
    "email": "alice@example.com"
}
''')

println json.name      // Dot notation
println json['age']    // Bracket notation
println json.active
println json.email
println json.getClass().name

Output

Alice
30
true
alice@example.com
org.apache.groovy.json.internal.LazyMap

What happened here: parseText() converted the JSON string into a LazyMap. You can access fields using dot notation (like a POJO) or bracket notation (like a map). The JSON boolean true became a Groovy Boolean, and the number 30 became an Integer.

Example 2: Parse Nested JSON Objects

What we’re doing: Navigating deeply nested JSON structures using chained dot notation.

Example 2: Nested JSON

import groovy.json.JsonSlurper

def slurper = new JsonSlurper()
def json = slurper.parseText('''
{
    "user": {
        "name": "Bob",
        "address": {
            "street": "123 Main St",
            "city": "Springfield",
            "state": "IL",
            "zip": "62701"
        },
        "preferences": {
            "theme": "dark",
            "notifications": {
                "email": true,
                "sms": false,
                "push": true
            }
        }
    }
}
''')

println json.user.name
println json.user.address.city
println json.user.address.zip
println json.user.preferences.theme
println json.user.preferences.notifications.email
println json.user.preferences.notifications.sms

Output

Bob
Springfield
62701
dark
true
false

What happened here: Each nested JSON object became a nested map. You simply chain dot notation to drill into the structure – json.user.preferences.notifications.email. No need for get() calls or casting. Groovy’s dynamic nature makes this incredibly readable.

Example 3: Parse JSON Arrays

What we’re doing: Parsing JSON arrays and iterating through them with Groovy collection methods.

Example 3: JSON Arrays

import groovy.json.JsonSlurper

def slurper = new JsonSlurper()

// Top-level JSON array
def colors = slurper.parseText('["red", "green", "blue", "yellow"]')
println "Colors: ${colors}"
println "First:  ${colors[0]}"
println "Last:   ${colors[-1]}"
println "Count:  ${colors.size()}"

// Array of objects
def users = slurper.parseText('''
[
    {"name": "Alice", "role": "admin"},
    {"name": "Bob", "role": "editor"},
    {"name": "Charlie", "role": "viewer"}
]
''')

users.each { user ->
    println "${user.name} is a ${user.role}"
}

// Extract just the names
def names = users.collect { it.name }
println "Names: ${names}"

Output

Colors: [red, green, blue, yellow]
First:  red
Last:   yellow
Count:  4
Alice is a admin
Bob is a editor
Charlie is a viewer
Names: [Alice, Bob, Charlie]

What happened here: A top-level JSON array becomes a Groovy List. When the array contains objects, each object becomes a map. You can use all the Groovy collection methods – each(), collect(), find(), findAll() – directly on the parsed result.

Example 4: Accessing Nested Arrays

What we’re doing: Working with JSON that contains arrays nested inside objects.

Example 4: Nested Arrays

import groovy.json.JsonSlurper

def slurper = new JsonSlurper()
def json = slurper.parseText('''
{
    "company": "TechCorp",
    "departments": [
        {
            "name": "Engineering",
            "employees": ["Alice", "Bob", "Charlie"]
        },
        {
            "name": "Marketing",
            "employees": ["Diana", "Eve"]
        },
        {
            "name": "Sales",
            "employees": ["Frank", "Grace", "Heidi", "Ivan"]
        }
    ]
}
''')

println "Company: ${json.company}"
println "Departments: ${json.departments.size()}"

// Access specific department
println "First dept: ${json.departments[0].name}"
println "Eng team:   ${json.departments[0].employees}"

// Spread operator to get all department names
println "All depts:  ${json.departments*.name}"

// Flatten all employees
def allEmployees = json.departments.collectMany { it.employees }
println "All staff:  ${allEmployees}"
println "Headcount:  ${allEmployees.size()}"

Output

Company: TechCorp
Departments: 3
First dept: Engineering
Eng team:   [Alice, Bob, Charlie]
All depts:  [Engineering, Marketing, Sales]
All staff:  [Alice, Bob, Charlie, Diana, Eve, Frank, Grace, Heidi, Ivan]
Headcount:  9

What happened here: The spread operator (*.) extracts a property from each element in a list. The collectMany() method flattens nested lists into one – perfect for gathering all items from multiple nested arrays.

Example 5: Type Coercion After Parsing

What we’re doing: Checking and coercing the types that JsonSlurper produces from different JSON value types.

Example 5: Type Coercion

import groovy.json.JsonSlurper

def slurper = new JsonSlurper()
def json = slurper.parseText('''
{
    "intValue": 42,
    "longValue": 9999999999,
    "decimalValue": 3.14159,
    "boolValue": true,
    "nullValue": null,
    "stringValue": "hello",
    "stringNumber": "42"
}
''')

println "int:     ${json.intValue}     -> ${json.intValue.getClass().name}"
println "long:    ${json.longValue}    -> ${json.longValue.getClass().name}"
println "decimal: ${json.decimalValue} -> ${json.decimalValue.getClass().name}"
println "bool:    ${json.boolValue}    -> ${json.boolValue.getClass().name}"
println "null:    ${json.nullValue}    -> ${json.nullValue == null}"
println "string:  ${json.stringValue}  -> ${json.stringValue.getClass().name}"

// Convert string to integer manually
def num = json.stringNumber as Integer
println "coerced: ${num} -> ${num.getClass().name}"

// Use Groovy's safe navigation for potentially null fields
println "safe:    ${json.nullValue?.toUpperCase()}"
println "missing: ${json.nonExistent}"

Output

int:     42     -> java.lang.Integer
long:    9999999999    -> java.lang.Long
decimal: 3.14159 -> java.math.BigDecimal
bool:    true    -> java.lang.Boolean
null:    null    -> true
string:  hello  -> java.lang.String
coerced: 42 -> java.lang.Integer
safe:    null
missing: null

What happened here: JsonSlurper automatically picks the right type. Small integers become Integer, large ones become Long, and decimals become BigDecimal (not Double – so you get exact precision). Accessing a missing key returns null rather than throwing an exception.

Example 6: Parse JSON from a File

What we’re doing: Reading and parsing a JSON file from the local filesystem.

Example 6: Parse JSON File

import groovy.json.JsonSlurper

// Create a sample JSON file first
def jsonContent = '''
{
    "database": {
        "host": "localhost",
        "port": 5432,
        "name": "myapp_db",
        "credentials": {
            "username": "admin",
            "password": "secret123"
        }
    }
}
'''
def tempFile = File.createTempFile('config', '.json')
tempFile.text = jsonContent
tempFile.deleteOnExit()

// Parse the file
def slurper = new JsonSlurper()
def config = slurper.parse(tempFile)

println "Host: ${config.database.host}"
println "Port: ${config.database.port}"
println "DB:   ${config.database.name}"
println "User: ${config.database.credentials.username}"

// You can also parse using a Reader
def config2 = slurper.parse(new FileReader(tempFile))
println "Via Reader - Host: ${config2.database.host}"

Output

Host: localhost
Port: 5432
DB:   myapp_db
User: admin
Via Reader - Host: localhost

What happened here: The parse(File) method reads and parses in one call. You don’t need to read the file content separately. You can also use parse(Reader) if you need more control over encoding or buffering.

Example 7: Parse JSON from a URL

What we’re doing: Fetching and parsing JSON directly from a URL (useful for simple API calls).

Example 7: Parse JSON from URL

import groovy.json.JsonSlurper

def slurper = new JsonSlurper()

// Parse directly from a URL
// def result = slurper.parse(new URL('https://api.example.com/data'))

// Simulating a URL parse with parseText for demonstration
def json = slurper.parseText('''
{
    "userId": 1,
    "id": 1,
    "title": "Sample Todo",
    "completed": false
}
''')

println "ID:        ${json.id}"
println "Title:     ${json.title}"
println "Completed: ${json.completed}"

// Alternative: read URL content first, then parse
// def text = new URL('https://api.example.com/data').text
// def result = slurper.parseText(text)

// Using URL.text with JsonSlurper (the most Groovy way)
// def todo = slurper.parseText('https://jsonplaceholder.typicode.com/todos/1'.toURL().text)
// println todo.title

Output

ID:        1
Title:     Sample Todo
Completed: false

What happened here: JsonSlurper can parse directly from a URL object. Alternatively, you can use Groovy’s URL.text property to fetch the content first, then pass it to parseText(). For more advanced HTTP handling, see our Groovy REST API Consumption guide.

Example 8: Filtering and Searching Parsed JSON

What we’re doing: Using Groovy collection methods to filter, search, and transform parsed JSON data.

Example 8: Filter and Search JSON

import groovy.json.JsonSlurper

def slurper = new JsonSlurper()
def products = slurper.parseText('''
[
    {"name": "Laptop",    "price": 999.99,  "category": "electronics", "inStock": true},
    {"name": "Book",      "price": 14.99,   "category": "education",   "inStock": true},
    {"name": "Phone",     "price": 699.99,  "category": "electronics", "inStock": false},
    {"name": "Tablet",    "price": 449.99,  "category": "electronics", "inStock": true},
    {"name": "Notebook",  "price": 4.99,    "category": "education",   "inStock": true},
    {"name": "Monitor",   "price": 349.99,  "category": "electronics", "inStock": false}
]
''')

// Find all electronics in stock
def available = products.findAll { it.category == 'electronics' && it.inStock }
println "Available electronics:"
available.each { println "  ${it.name} - \$${it.price}" }

// Find the cheapest product
def cheapest = products.min { it.price }
println "\nCheapest: ${cheapest.name} (\$${cheapest.price})"

// Total value of in-stock items
def totalValue = products.findAll { it.inStock }.sum { it.price }
println "In-stock value: \$${totalValue}"

// Group by category
def grouped = products.groupBy { it.category }
grouped.each { category, items ->
    println "\n${category}: ${items*.name}"
}

Output

Available electronics:
  Laptop - $999.99
  Tablet - $449.99

Cheapest: Notebook ($4.99)
In-stock value: $1469.96

electronics: [Laptop, Phone, Tablet, Monitor]
education: [Book, Notebook]

What happened here: Once JSON is parsed into maps and lists, you have the full power of Groovy’s collection methods at your disposal. findAll() filters, min() finds the smallest by a given criteria, sum() aggregates, and groupBy() categorizes. This is one of the biggest advantages of Groovy’s approach to JSON.

Example 9: Parse JSON with Special Characters and Unicode

What we’re doing: Handling JSON that contains escape sequences, Unicode characters, and special strings.

Example 9: Special Characters

import groovy.json.JsonSlurper

def slurper = new JsonSlurper()
def json = slurper.parseText('''
{
    "escaped_quotes": "She said \\"hello\\"",
    "newline": "line1\\nline2",
    "tab": "col1\\tcol2",
    "unicode": "caf\\u00e9",
    "emoji": "Hello \\ud83d\\ude00",
    "backslash": "C:\\\\Users\\\\test",
    "empty": "",
    "spaces": "  trimmed  "
}
''')

println "Quotes:    ${json.escaped_quotes}"
println "Newline:   ${json.newline}"
println "Tab:       ${json.tab}"
println "Unicode:   ${json.unicode}"
println "Emoji:     ${json.emoji}"
println "Backslash: ${json.backslash}"
println "Empty:     '${json.empty}'"
println "Spaces:    '${json.spaces}'"
println "Trimmed:   '${json.spaces.trim()}'"

Output

Quotes:    She said "hello"
Newline:   line1
line2
Tab:       col1	col2
Unicode:   café
Emoji:     Hello 😀
Backslash: C:\Users\test
Empty:     ''
Spaces:    '  trimmed  '
Trimmed:   'trimmed'

What happened here: JsonSlurper correctly handles all JSON escape sequences including \", \n, \t, \\, and \uXXXX Unicode escapes. The parsed strings are regular Groovy strings, so you can apply any string methods like trim() or toUpperCase().

Example 10: Error Handling During JSON Parsing

What we’re doing: Gracefully handling malformed JSON and missing fields.

Example 10: Error Handling

import groovy.json.JsonSlurper
import groovy.json.JsonException

def slurper = new JsonSlurper()

// Handle malformed JSON
def badJsonSamples = [
    '{"name": "Alice", "age": }',           // Missing value
    '{"name": "Alice" "age": 30}',           // Missing comma
    '{name: "Alice"}',                        // Unquoted key (invalid in strict mode)
    'not json at all',                        // Not JSON
    ''                                        // Empty string
]

badJsonSamples.each { sample ->
    try {
        def result = slurper.parseText(sample)
        println "Parsed: ${result}"
    } catch (JsonException e) {
        println "JsonException: ${e.message.take(60)}..."
    } catch (Exception e) {
        println "${e.class.simpleName}: ${e.message?.take(60)}..."
    }
}

// Safe field access with defaults
println "\n--- Safe field access ---"
def json = slurper.parseText('{"name": "Alice"}')
println "Name:    ${json.name ?: 'Unknown'}"
println "Age:     ${json.age ?: 'Not specified'}"
println "Email:   ${json.email ?: 'N/A'}"

// Safe nested access
def config = slurper.parseText('{"db": {}}')
println "Host:    ${config.db?.host ?: 'localhost'}"
println "Port:    ${config.db?.port ?: 5432}"

Output

JsonException: Unable to determine the current character, i...
JsonException: Unable to determine the current character, i...
Parsed: [name:Alice]
JsonException: Unable to determine the current character, i...
IllegalArgumentException: Text must not be null or empty...

--- Safe field access ---
Name:    Alice
Age:     Not specified
Email:   N/A
Host:    localhost
Port:    5432

What happened here: Malformed JSON throws JsonException. Always wrap parse calls in try-catch when dealing with external data. For missing fields, use the Elvis operator (?:) for defaults and safe navigation (?.) for nested access. Note that unquoted keys may parse successfully with the default LAX-like parser.

Example 11: Parse JSON with Different Parser Types

What we’re doing: Exploring the different parser types available in JsonSlurper and when to use each one.

Example 11: Parser Types

import groovy.json.JsonSlurper
import groovy.json.JsonParserType

def jsonText = '{"name": "Alice", "scores": [95, 87, 92]}'

// INDEX_OVERLAY - default, best for most cases
def slurper1 = new JsonSlurper().setType(JsonParserType.INDEX_OVERLAY)
def result1 = slurper1.parseText(jsonText)
println "INDEX_OVERLAY: ${result1.name}, scores=${result1.scores}"

// CHARACTER_SOURCE - best for large files from streams
def slurper2 = new JsonSlurper().setType(JsonParserType.CHARACTER_SOURCE)
def result2 = slurper2.parseText(jsonText)
println "CHAR_SOURCE:   ${result2.name}, scores=${result2.scores}"

// LAX - allows comments and unquoted keys
def slurper3 = new JsonSlurper().setType(JsonParserType.LAX)
def laxJson = '''
{
    // This is a comment (not valid in strict JSON!)
    name: "Bob",
    age: 25,
    'nickname': 'Bobby'
}
'''
def result3 = slurper3.parseText(laxJson)
println "LAX:           ${result3.name}, age=${result3.age}, nick=${result3.nickname}"

// CHAR_BUFFER - uses char buffer internally
def slurper4 = new JsonSlurper().setType(JsonParserType.CHAR_BUFFER)
def result4 = slurper4.parseText(jsonText)
println "CHAR_BUFFER:   ${result4.name}, scores=${result4.scores}"

Output

INDEX_OVERLAY: Alice, scores=[95, 87, 92]
CHAR_SOURCE:   Alice, scores=[95, 87, 92]
LAX:           Bob, age=25, nick=Bobby
CHAR_BUFFER:   Alice, scores=[95, 87, 92]

What happened here: The LAX parser is the most forgiving – it accepts comments, unquoted keys, and single-quoted strings. INDEX_OVERLAY is the fastest for most use cases. Use CHARACTER_SOURCE when parsing from large streams or readers.

Example 12: Real-World Example – Processing an API Response

What we’re doing: Simulating a real API response and extracting meaningful data from a complex JSON structure.

Example 12: Real-World API Response

import groovy.json.JsonSlurper

def slurper = new JsonSlurper()
def response = slurper.parseText('''
{
    "status": "success",
    "data": {
        "page": 1,
        "total_pages": 5,
        "per_page": 3,
        "total": 15,
        "results": [
            {
                "id": 101,
                "title": "Introduction to Groovy",
                "author": {"name": "Jane Doe", "email": "jane@example.com"},
                "tags": ["groovy", "jvm", "tutorial"],
                "published": true,
                "views": 15420
            },
            {
                "id": 102,
                "title": "Advanced JSON Parsing",
                "author": {"name": "John Smith", "email": "john@example.com"},
                "tags": ["groovy", "json", "advanced"],
                "published": true,
                "views": 8930
            },
            {
                "id": 103,
                "title": "Draft: Groovy DSLs",
                "author": {"name": "Jane Doe", "email": "jane@example.com"},
                "tags": ["groovy", "dsl"],
                "published": false,
                "views": 0
            }
        ]
    }
}
''')

// Check response status
if (response.status == 'success') {
    def data = response.data
    println "Page ${data.page} of ${data.total_pages} (${data.total} total articles)"
    println "-" * 50

    // Only published articles
    def published = data.results.findAll { it.published }
    published.each { article ->
        println "  [#${article.id}] ${article.title}"
        println "    By: ${article.author.name}"
        println "    Tags: ${article.tags.join(', ')}"
        println "    Views: ${article.views}"
        println()
    }

    // Most viewed article
    def topArticle = data.results.max { it.views }
    println "Most viewed: '${topArticle.title}' (${topArticle.views} views)"

    // All unique tags
    def allTags = data.results.collectMany { it.tags }.unique().sort()
    println "All tags: ${allTags}"
}

Output

Page 1 of 5 (15 total articles)
--------------------------------------------------
  [#101] Introduction to Groovy
    By: Jane Doe
    Tags: groovy, jvm, tutorial
    Views: 15420

  [#102] Advanced JSON Parsing
    By: John Smith
    Tags: groovy, json, advanced
    Views: 8930

Most viewed: 'Introduction to Groovy' (15420 views)
All tags: [advanced, dsl, groovy, json, jvm, tutorial]

What happened here: This is a realistic scenario – parsing a paginated API response with nested objects, arrays, and metadata. We checked the status, filtered for published articles, extracted nested author names, flattened all tags, and found the most popular article. This is where Groovy JsonSlurper truly shines – doing all of this in a few lines of readable code.

Lazy vs Non-Lazy Parsing

By default, JsonSlurper uses the INDEX_OVERLAY parser, which creates lazy maps. This means values aren’t fully parsed until you actually access them. This is great for performance when you only need a subset of the data, but it can cause unexpected behavior in some cases.

Lazy vs Non-Lazy Parsing

import groovy.json.JsonSlurper
import groovy.json.JsonParserType

def jsonText = '{"name": "Alice", "age": 30, "scores": [95, 87, 92]}'

// Default lazy parsing (INDEX_OVERLAY)
def lazySlurper = new JsonSlurper()
def lazyResult = lazySlurper.parseText(jsonText)
println "Lazy map type:  ${lazyResult.getClass().name}"

// Non-lazy parsing (CHARACTER_SOURCE)
def eagerSlurper = new JsonSlurper().setType(JsonParserType.CHARACTER_SOURCE)
def eagerResult = eagerSlurper.parseText(jsonText)
println "Eager map type: ${eagerResult.getClass().name}"

// Both produce identical results for reading
println "Lazy name:  ${lazyResult.name}"
println "Eager name: ${eagerResult.name}"

// But lazy maps are NOT thread-safe for the first access
// If sharing parsed data across threads, convert to a regular map:
def safeMap = new HashMap(lazyResult)
println "Safe map type: ${safeMap.getClass().name}"
println "Safe name:     ${safeMap.name}"

Output

Lazy map type:  org.apache.groovy.json.internal.LazyMap
Eager map type: java.util.LinkedHashMap
Lazy name:  Alice
Eager name: Alice
Safe map type: java.util.HashMap
Safe name:     Alice

The bottom line: if you’re parsing JSON in a single thread and reading it immediately, the default lazy parser is fine and faster. If you need to store the result for later use or share it across threads, either use CHARACTER_SOURCE or wrap the result in a new HashMap(lazyResult).

Edge Cases and Best Practices

Edge Case: Duplicate Keys

Duplicate Keys

import groovy.json.JsonSlurper

def slurper = new JsonSlurper()

// JSON with duplicate keys - last value wins
def json = slurper.parseText('{"name": "Alice", "name": "Bob"}')
println "Name: ${json.name}"  // Bob - last value wins

// Empty JSON object and array
def emptyObj = slurper.parseText('{}')
def emptyArr = slurper.parseText('[]')
println "Empty obj: ${emptyObj} (size: ${emptyObj.size()})"
println "Empty arr: ${emptyArr} (size: ${emptyArr.size()})"

// Deeply nested JSON (stress test)
def deep = slurper.parseText('{"a":{"b":{"c":{"d":{"e":"deep value"}}}}}')
println "Deep: ${deep.a.b.c.d.e}"

Output

Name: Bob
Empty obj: [:] (size: 0)
Empty arr: [] (size: 0)
Deep: deep value

Best Practices

DO:

  • Reuse JsonSlurper instances – they are thread-safe
  • Use ?. (safe navigation) and ?: (Elvis operator) for optional fields
  • Wrap external JSON parsing in try-catch blocks
  • Use LAX parser type when dealing with non-standard JSON (e.g., JSON with comments)
  • Convert lazy maps to regular maps when storing results long-term

DON’T:

  • Assume all JSON numbers are Integer – large numbers become Long or BigDecimal
  • Share lazy-parsed results across threads without converting first
  • Catch Exception when JsonException is more specific
  • Parse very large JSON files (100MB+) in one go – use a streaming parser instead

Common Pitfalls

Pitfall 1: Lazy Map Mutation

Problem: Trying to modify a lazy map and getting unexpected results.

Pitfall 1: Lazy Map Issues

import groovy.json.JsonSlurper

def slurper = new JsonSlurper()
def json = slurper.parseText('{"name": "Alice", "age": 30}')

// This works - but the result is a LazyMap
json.email = "alice@example.com"
println json  // May have unexpected ordering

// Fix: convert to a regular map first
def map = new LinkedHashMap(slurper.parseText('{"name": "Bob", "age": 25}'))
map.email = "bob@example.com"
println map

Output

[name:Alice, age:30, email:alice@example.com]
[name:Bob, age:25, email:bob@example.com]

Solution: If you plan to modify parsed JSON, wrap the result in a new LinkedHashMap() or new HashMap() first to get a standard mutable map.

Pitfall 2: Number Type Assumptions

Problem: Assuming all JSON integers parse to int.

Pitfall 2: Number Types

import groovy.json.JsonSlurper

def slurper = new JsonSlurper()
def json = slurper.parseText('''
{
    "small": 42,
    "big": 9999999999999,
    "decimal": 19.99,
    "scientific": 1.5e10
}
''')

println "${json.small}    -> ${json.small.getClass().name}"
println "${json.big}      -> ${json.big.getClass().name}"
println "${json.decimal}  -> ${json.decimal.getClass().name}"
println "${json.scientific} -> ${json.scientific.getClass().name}"

// This can cause issues if you explicitly cast
// int x = json.big  // This would overflow!
long safe = json.big as long
println "Safe long: ${safe}"

Output

42    -> java.lang.Integer
9999999999999      -> java.lang.Long
19.99  -> java.math.BigDecimal
1.5E+10 -> java.math.BigDecimal
Safe long: 9999999999999

Solution: Never assume the numeric type. Use as long, as BigDecimal, or check the type with instanceof if type precision matters.

Pitfall 3: Null vs Missing Key

Problem: Not distinguishing between a field that is explicitly null and a field that is missing.

Pitfall 3: Null vs Missing

import groovy.json.JsonSlurper

def slurper = new JsonSlurper()
def json = slurper.parseText('{"name": "Alice", "email": null}')

// Both return null - but for different reasons!
println "email:   ${json.email}"       // null - field exists but value is null
println "phone:   ${json.phone}"       // null - field does not exist

// How to distinguish:
println "has email? ${json.containsKey('email')}"   // true
println "has phone? ${json.containsKey('phone')}"   // false

Output

email:   null
phone:   null
has email? true
has phone? false

Solution: Use containsKey() to check whether a field exists in the parsed JSON, even if its value is null.

Conclusion

Groovy JSON parsing with JsonSlurper is one of the cleanest JSON parsing experiences on the JVM. No annotations, no POJOs, no configuration – just parse and go. It handles config files, API responses, and data pipelines equally well – JsonSlurper turns JSON into native Groovy data structures that you can slice, dice, filter, and transform with all the collection methods you already know.

The key is understanding the type mappings (objects become maps, arrays become lists, numbers become Integer/Long/BigDecimal), knowing when to use the LAX parser for non-standard JSON, and handling lazy maps correctly in multithreaded scenarios.

Now that you can parse JSON, the next step is generating it. Head over to our Groovy JSON Output with JsonBuilder guide to learn how to build JSON from Groovy objects, maps, and custom structures. And for end-to-end API work, check out Groovy REST API Consumption.

Summary

  • JsonSlurper is built into Groovy – no external dependencies needed
  • JSON objects parse to maps, JSON arrays parse to lists – use dot notation and indexing
  • Use parseText() for strings, parse(File) for files, parse(URL) for remote JSON
  • Numbers map to Integer, Long, or BigDecimal – never assume int
  • Use LAX parser for non-standard JSON with comments or unquoted keys
  • Convert lazy maps to regular maps when sharing across threads or storing long-term

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 JSON Output with JsonBuilder

Frequently Asked Questions

What is JsonSlurper in Groovy?

JsonSlurper is Groovy’s built-in JSON parser from the groovy.json package. It parses JSON strings, files, URLs, and streams into native Groovy data structures – maps for JSON objects and lists for JSON arrays. No external libraries are needed, and you access parsed fields using dot notation or bracket notation.

How do I parse a JSON string in Groovy?

Create a JsonSlurper instance and call parseText(): def json = new JsonSlurper().parseText('{"name": "Alice"}') . Then access fields with json.name. You can also parse files with parse(File), URLs with parse(URL), and streams with parse(InputStream).

What types does JsonSlurper return for JSON numbers?

JsonSlurper maps JSON integers to java.lang.Integer for small values and java.lang.Long for larger values. Decimal numbers become java.math.BigDecimal, which provides exact precision. Never assume all JSON numbers will be int – always check or cast explicitly when type precision matters.

What is the difference between lazy and non-lazy JSON parsing in Groovy?

The default INDEX_OVERLAY parser creates lazy maps (LazyMap) where values aren’t fully materialized until accessed. This is faster when you only need a subset of the data. The CHARACTER_SOURCE parser creates regular LinkedHashMap instances eagerly. Use eager parsing when sharing results across threads or storing them long-term.

How do I handle malformed JSON in Groovy?

Wrap your parseText() or parse() call in a try-catch block catching groovy.json.JsonException. For missing fields, use the Elvis operator (json.field ?: 'default') and safe navigation (json?.nested?.field). Use containsKey() to distinguish between a field that is explicitly null and a field that is missing entirely.

Previous in Series: Groovy Builder Pattern Annotation

Next in Series: Groovy JSON Output with JsonBuilder

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 *