How do I verify a value buried deep inside a JSON response?

I’m trying to verify my API response, but the JSON I get back is a total mess of brackets and braces. I can see the accountBalance value when I look at the response in the “Console” or “Log Viewer,” but I don’t know how to tell Katalon to go find it.

The JSON isn’t just a simple list; it’s nested! It looks something like data, then inside that is accountDetails, and then inside that is an array of objects, and the balance is inside one of those. I tried using WS.verifyElementPropertyValue, but I keep getting errors like “Expected value is null” because I’m definitely not typing the path correctly. Is there an easier way to “drill down” into these layers without guessing the path every time?

Navigating nested JSON structures is a fundamental skill in API automation. To do this reliably, we use GPath (Groovy Path) expressions. Think of GPath as “XPath for JSON.”

Instead of guessing the path, you should use the Object Repository’s “Verification” tab or a dedicated JSON parser. In professional testing, we use JsonSlurper to turn that “mess of brackets” into a searchable Map/List structure. This allows us to use dot notation (e.g., data.account.balance) to reach any depth.

The Solution: Using GPath and JsonSlurper

If your JSON looks like this:

JSON

{
  "data": {
    "accountDetails": [
      { "type": "Savings", "balance": 5000.00 }
    ]
  }
}

Your path is: data.accountDetails[0].balance

Custom Keyword Helper: The JSON Navigator

This keyword allows you to pass a “dot-notation” path (like a GPS coordinate) to find any value within your API response instantly.

Groovy

import com.kms.katalon.core.annotation.Keyword
import com.kms.katalon.core.testobject.ResponseObject
import com.kms.katalon.core.util.KeywordUtil
import groovy.json.JsonSlurper

class ApiValidator {

    /**
     * Verifies a nested JSON value using a GPath string
     * @param response The API ResponseObject
     * @param jsonPath The path (e.g., "data.accountDetails[0].balance")
     * @param expectedValue The value you expect to find
     */
    @Keyword
    def verifyNestedValue(ResponseObject response, String jsonPath, Object expectedValue) {
        // Parse the response text into a Groovy object
        def jsonResponse = new JsonSlurper().parseText(response.getResponseText())
        
        // Use Groovy's dynamic GPath to find the value
        def actualValue = jsonPath.split('\\.').inject(jsonResponse) { obj, key -> 
            if (key.contains("[")) {
                def listKey = key.substring(0, key.indexOf("["))
                def index = Integer.parseInt(key.substring(key.indexOf("[") + 1, key.indexOf("]")))
                return obj."$listKey"[index]
            }
            return obj."$key" 
        }

        if (actualValue.toString() == expectedValue.toString()) {
            KeywordUtil.markPassed("Found expected value: " + expectedValue)
        } else {
            KeywordUtil.markFailed("Verification failed! Expected: ${expectedValue} but found: ${actualValue}")
        }
    }
}

How to use it in your Script:

Groovy

def response = WS.sendRequest(findTestObject('Object Repository/API/GetAccount'))

// Use the dot-notation path to verify the 3rd layer of the JSON
CustomKeywords.'ApiValidator.verifyNestedValue'(response, "data.accountDetails[0].balance", 5000.00)

Architect’s Tip: If you are unsure of the path, copy your JSON into an online “JSON Path Finder” tool. It will give you the exact string you need to plug into the keyword. Also, remember that JSON arrays (items inside []) are zero-indexed, so the first item is always [0].

Do you have a sample of the JSON response? I can help you write the exact path string if the structure is particularly complex!

hi @ebauch

maybe you can try to use JsonSlurper into your project

parse the response with JsonSlurper and inspect it in the Variables view to find the exact path.

import groovy.json.JsonSlurper

def json = new JsonSlurper().parseText(response.getResponseBodyContent())
def actualBalance = json.data.accountDetails[0].accountBalance

for nested arrays, use index brackets like data.accountDetails[0].accountBalance. Asserting on the parsed object directly is usually cleaner than WS.verifyElementPropertyValue for complex responses

I will show you a firm way to traverse an object tree derived from a JSON. It works but isn’t fancy like GUI tools.

Create a Test Case, copy & paste the following code:

import com.kms.katalon.core.webservice.keyword.WSBuiltInKeywords as WS

import groovy.json.JsonOutput
import groovy.json.JsonSlurper

String json = """
{
  "data": {
	"owner": "I my me mine",
    "address": "somewhere",
    "accountDetails": [
      { "foo": "foo value", "balance": 123 },
      { "bar": "bar value", "balance": 456 },
      { "buz": "buz value", "balance": 789},
      { "type": "Savings", "balance": 5000.00 }
    ]
  }
}
"""

def slurper = new JsonSlurper()
def parsed = slurper.parseText(json)

//def item = parsed
//def item = parsed["data"]
//def item = parsed["data"]["owner"]
//def item = parsed["data"]["accountDetails"]
//def item = parsed["data"]["accountDetails"][0]
//def item = parsed["data"]["accountDetails"][1]
//def item = parsed["data"]["accountDetails"][2]
//def item = parsed["data"]["accountDetails"][3]
//def item = parsed["data"]["accountDetails"][3]["balance"]

String result = JsonOutput.toJson(item)
if (result.startsWith('[') || result.startsWith('{')) {
	println JsonOutput.prettyPrint(result)
} else {
	println result
}

You will want to edit this script a little, then run it to see the result.

parsed[“data”]

Uncoment the line of

def item = parsed["data"]

You run it, then you will see the following output in the Console:

parsed[“data”][“accountDetails”]

You want to comment out the previous line //def item = parsed["data"] back. And you want to uncomment another line

def item = parsed["data"]["accountDetails"]

You run it, then you will see the following output in the Console:

2026-04-25 22:25:43.345 DEBUG testcase.TC1                             - 1: println(JsonOutput.prettyPrint(result))
[
    {
        "foo": "foo value",
        "balance": 123
    },
    {
        "bar": "bar value",
        "balance": 456
    },
    {
        "buz": "buz value",
        "balance": 789
    },
    {
        "type": "Savings",
        "balance": 5000.00
    }
]
2026-04-25 22:25:43.372 INFO  c.k.katalon.core.main.TestCaseExecutor   - END Test Cases/TC1

You got a bit closer to the target.

parsed[“data”][“accountDetails”][0]

You want to comment out the previous line. You want to uncomment another line

def item = parsed["data"]["accountDetails"][0]

You run it, then you will see the following output in the Console:

2026-04-25 22:28:31.313 DEBUG testcase.TC1                             - 1: println(JsonOutput.prettyPrint(result))
{
    "foo": "foo value",
    "balance": 123
}
2026-04-25 22:28:31.339 INFO  c.k.katalon.core.main.TestCaseExecutor   - END Test Cases/TC1

Ah! You missed the target! You want the one in the following sibling.

parsed[“data”][“accountDetails”][3]

You want to comment out the previous line. You want to uncomment another line:

def item = parsed["data"]["accountDetails"][3]

You run it. Then you will see the following

2026-04-25 22:31:09.242 DEBUG testcase.TC1                             - 1: println(JsonOutput.prettyPrint(result))
{
    "type": "Savings",
    "balance": 5000.00
}
2026-04-25 22:31:09.276 INFO  c.k.katalon.core.main.TestCaseExecutor   - END Test Cases/TC1

Closer, closer.

parsed[“data”][“accountDetails”][3][“balance”]

You want to comment out the previous line. You want to uncomment another line:

def item = parsed["data"]["accountDetails"][3]["balance"]

You run it. You will see the following result.

2026-04-25 22:32:07.208 DEBUG testcase.TC1                             - 1: println(result)
5000.00
2026-04-25 22:32:07.227 INFO  c.k.katalon.core.main.TestCaseExecutor   - END Test Cases/TC1

You successfully got the number you want.

Conclusion

See https://www.baeldung.com/groovy-json for JsonSlurper and JsonOutput.

With certain programming skills, you can move around the target object and eventually come up with a solution.

This usually happens because the API response is nested and contains arrays, and WS.verifyElementPropertyValue() requires an exact JSON path (including array indexes). If the path is slightly wrong, Katalon returns null.

Best way would be to parse the response using Groovy instead of guessing paths:
import groovy.json.JsonSlurper

def json = new JsonSlurper().parseText(response.getResponseBodyContent())

def balance = json.data.accountDetails[0].accountBalance

assert balance == 5000