1. Overview

Working with JSON in Java often requires accessing nested keys. Jackson, a popular JSON processing library, provides a convenient way to achieve this using the findValue() method.

In this tutorial, we’ll explore using the findValue() method to retrieve values for nested keys.

2. Understanding Nested Keys in JSON

JSON objects can have nested structures, making it challenging to access deeply nested values:

{
  "user": {
    "id": 1,
    "name": "John Doe",
    "contact": {
      "email": "[email protected]",
      "phone": "123-456-7890"
    }
  }
}

In this JSON, the email key is nested within the contact object, which is nested within the user object.

3. Using the  findValue() Method

The findValue() method in Jackson allows us to search for a specific key within a JSON tree and retrieve its associated value.

First, we’ll convert the JSON string into a JsonNode using the ObjectMapper, creating a tree representation of the JSON data:

String jsonString = "{ \"user\": { \"id\": 1, \"name\": \"John Doe\", \"contact\": { \"email\": \"[email protected]\", \"phone\": \"123-456-7890\" } } }";
ObjectMapper mapper = new ObjectMapper();
JsonNode rootNode = mapper.readTree(jsonString);

We can then use the findValue() method to search for the email key within the rootNode:

String email = rootNode.findValue("email").asText();
assertEquals("[email protected]", email);

Here we’ve used asText() to convert the JsonNode containing the email into a string.

4. Handling Missing Keys Gracefully

When working with JSON, we may encounter scenarios where a key is missing. The findValue() method returns null when the key isn’t found in the JSON structure.

Consider the following JSON where the email key is missing:

String jsonString = "{ \"user\": { \"id\": 1, \"name\": \"John Doe\", \"contact\": { \"phone\": \"123-456-7890\" } } }";
ObjectMapper mapper = new ObjectMapper();
JsonNode rootNode = mapper.readTree(jsonString);
JsonNode emailNode = rootNode.findValue("email");

In this example, findValue(“email”) returns null because the email key isn’t in the JSON. Furthermore, we can verify this by asserting that emailNode is null:

assertNull(emailNode);

5. Using the findValue() Method With Arrays

The findValue() method also works with arrays in JSON:

{
  "users": [
    { "id": 1, "name": "John Doe", "contact": { "email": "[email protected]" } },
    { "id": 2, "name": "Jane Doe", "contact": { "email": "[email protected]" } }
  ]
}

Let’s parse the JSON string:

String jsonString = "{ \"users\": [ { \"id\": 1, \"name\": \"John Doe\", \"contact\": { \"email\": \"[email protected]\" } }, { \"id\": 2, \"name\": \"Jane Doe\", \"contact\": { \"email\": \"[email protected]\" } } ] }";
ObjectMapper mapper = new ObjectMapper();
JsonNode rootNode = mapper.readTree(jsonString);

We then use findValues() to search for all occurrences of the email key:

List<String> emails = rootNode.findValues("email")
    .stream()
    .map(JsonNode::asText)
    .collect(Collectors.toList());
assertThat(emails)
  .containsExactly("[email protected]", "[email protected]");

Here, the findValues() method returns a list of JsonNode objects containing an email address. Then we convert these nodes to strings and collect them into a list and check it has the expected email addresses in the correct order.

6. Handling Deeply-Nested Keys

Rather than find a key by name somewhere in the JSON structure, we can use the at() method to target fields in deeply nested JSON structures at a specific path.

Let’s consider the following JSON:

{
  "organization": {
    "department": {
      "team": {
        "lead": {
          "name": "Alice",
          "contact": {
            "email": "[email protected]"
          }
        }
      }
    }
  }
}

To retrieve the email address of the team lead, we first parse the JSON string into a JsonNode:

String jsonString = "{ \"organization\": { \"department\": { \"team\": { \"lead\": { \"name\": \"Alice\", \"contact\": { \"email\": \"[email protected]\" } } } } } }";
ObjectMapper mapper = new ObjectMapper();
JsonNode rootNode = mapper.readTree(jsonString);

We then use the at() method to navigate through the nested structure:

String email = rootNode.at("/organization/department/team/lead/contact/email").asText();
assertEquals("[email protected]", email);

Here, the path provided to the at() method is a JSON Pointer, which is a standardized way to navigate through a JSON document using string syntax.

7. Conclusion

Jackson’s findValue() method offers a powerful and flexible way to retrieve values for nested keys in JSON.

Whether dealing with deeply nested objects or arrays, findValue() simplifies the process and enhances code readability and maintainability. However, for the precise location of a field, we can use the at() function with a JSON path.

As usual, we can find the full source code and examples over on GitHub.