Enterprise Java

Find Nested Key via Jackson Example

1. Introduction

JavaScript Object Notation (JSON) is a text-based format for storing and exchanging data. It’s commonly used to transfer data between a server and a web application. Working with JSON in Java often requires accessing nested keys. Jackson is an open-source Java library developed by FasterXML, LLC for JSON processing. In this example, I will demonstrate findvalue of a nested key via JsonNode’s findValue() and path() methods.

  • abstract JsonNode findValue(String fieldName) – finding the first JSON Object field with the specified name in this node or its child nodes. It returns null if not found.
  • abstract JsonNode path(String fieldName) – accessing the specified field of an object node by the field name. It returns a “missing node” instead of null if not found.

2. Setup

In this step, I will create a gradle project along with Jackson and Junit libraries.

build.gradle

/*
 * This file was generated by the Gradle 'init' task.
 *
 * This generated file contains a sample Java application project to get you started.
 * For more details on building Java & JVM projects, please refer to https://docs.gradle.org/8.8/userguide/building_java_projects.html in the Gradle documentation.
 */

plugins {
    // Apply the application plugin to add support for building a CLI application in Java.
    id 'application'
}

repositories {
    // Use Maven Central for resolving dependencies.
    mavenCentral()
}

dependencies {
    // Use JUnit Jupiter for testing.
    testImplementation libs.junit.jupiter

    //use jackson
    implementation 'com.fasterxml.jackson.core:jackson-core:2.18.0'
    implementation 'com.fasterxml.jackson.core:jackson-databind:2.18.0'

    testRuntimeOnly 'org.junit.platform:junit-platform-launcher'

    // This dependency is used by the application.
    implementation libs.guava
}

// Apply a specific Java toolchain to ease working on different environments.
java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(17)
    }
}

application {
    // Define the main class for the application.
    mainClass = 'org.example.App'
}

tasks.named('test') {
    // Use JUnit Platform for unit tests.
    useJUnitPlatform()
}

3. FindValue Nested Key

In this step, I will create a FindNodeByValue.java that finds a nested node via the JsonNode.findValue method.

FindNodeByValue.java

package org.example;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

public class FindNodeByValue {

    public JsonNode findNestedNode(final String sourceJsonString, final String... paths) {
        ObjectMapper ob = new ObjectMapper();

        boolean found = false;
        JsonNode foundNode ;
        int i = 0;

        try {
            JsonNode rootNode = ob.readTree(sourceJsonString);
            foundNode = rootNode.findValue(paths[0]);

            while (!found) {
                if (foundNode == null) {
                    throw new IllegalArgumentException("Not found " + paths[i]);
                } else {
                    if (foundNode.isContainerNode()) {
                        if( i + 1 >= paths.length){
                            break;
                        }
                        foundNode = foundNode.findValue(paths[i + 1]);
                    } else {
                        found = true;
                    }
                }
                i++;
            }

        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
        return foundNode;
    }

}
  • Line 9: this method will find the nested key based on the given paths.
  • Line 18, 28: utilize the findValue method to find the field from JSON.
  • Line 21: when the field is not found, findValue returns null.

4. Find Nested Node via Path

In this step, I will create a FindNodeByPath.java that finds a nested node via Jackson JsonNode.path method. Note: this class is very similar to the FindNodeByValue class created in step 3. The only difference is that the path method returns a “missing node” object while the findValue method returns null if not found.

FindNodeByPath.java

package org.example;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

public class FindNodeByPath {

    public JsonNode findNestedNode(final String sourceJsonString, final String... paths) {
        ObjectMapper ob = new ObjectMapper();

        boolean found = false;
        JsonNode foundNode ;
        int i = 0;

        try {
            JsonNode rootNode = ob.readTree(sourceJsonString);
            foundNode = rootNode.path(paths[0]);
            while (!found) {
                if (foundNode.isMissingNode()) {
                    throw new IllegalArgumentException("Not found " + paths[i]);
                } else {
                    if (foundNode.isContainerNode()) {
                        if( i + 1 >= paths.length){
                            break;
                        }
                        foundNode = foundNode.path(paths[i + 1]);
                    } else {
                        found = true;
                    }
                }
                i++;
            }


        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
        return foundNode;
    }

}
  • Line 18, 27: utilize the path method to find the field.
  • Line 20: when the field is not found, it returns JsonNode with isMissingNode as true.

5. Demo with Junit Tests

5.1 Test Data

In this step, I will create a TestData.java test that defines five testing JSON strings and they will be used in steps 5.2 and 5.3.

TestData.java

package org.example;

public class TestData {
    public static final String JSON_1 = "{\"root\":\"rootValue\"}";
    public static final String JSON_2 = "{\"root\":{\"ele2\":\"mary\"}}";
    public static final String JSON_3 = "{\"root\":{\"ele2\":{\"ele3\":\"zheng\"}}}";
    public static final String JSON_4 = "{\"root\":{\"ele2\":{\"ele3\":{\"ele4\":\"jcg\"}}}}";
    public static final String JSON_5 = "{\"root\":{\"ele2\":{\"test\":{\"ele2\":\"jcg\"}}}}";
}

Here is the pretty formatted JSON_1. The root field is not nested and it has a simple string value of "rootValue".

JSON_1

{"root":"rootValue"}
  • It will be used in both steps 5.2 and 5.3 with the test_findNestedNode test.

Here is the pretty formatted JSON_2. Its root field is a nested key with a field name as ele2.

JSON_2

{
    "root": {
        "ele2": "mary"
    }
}
  • Line 3: It will be used in both steps 5.2 and 5.3 with the test_findNestedNode_2 test.

Here is the pretty formatted JSON_3. It has three levels of depth for a nested key: ele3.

JSON_3

{
    "root": {
        "ele2": {
            "ele3": "zheng"
        }
    }
}
  • Line 4: It will be used in both steps 5.2 and 5.3 with the test_findNestedNode_3 test.

Here is the pretty formatted JSON_4 which outlines the nested ele4. It will be used in both steps 5.2 and 5.3 with the test_findNestedNode test.

JSON_4

{
    "root": {
        "ele2": {
            "ele3": {
                "ele4": "jcg"
            }
        }
    }
}
  • Line 5: It will be used in both steps 5.2 and 5.3 with the test_findNestedNode_4 test.

Here is the pretty formatted JSON_5 which shows the ele2 shows at two different levels.

JSON_5

{
    "root": {
        "ele2": {
            "test": {
                "ele2": "jcg"
            }
        }
    }
}
  • Line 3, 5: It will be used in both steps 5.2 and 5.3 with the test_findNestedNode_5 test.

5.2 FindValue Nested Key Test

In this step, I will create a FindNodeByValueTest.java test that verifies finding a nested key via the findValue method with six tests to find the nested key at a various depth.

FindNoeByValueTest.java

package org.example;

import com.fasterxml.jackson.databind.JsonNode;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;

class FindNodeByValueTest {
    FindNodeByValue findNestedNode = new FindNodeByValue();


    @Test
    void test_findNestedNode() {

        JsonNode firstElement = findNestedNode.findNestedNode(TestData.JSON_1, "root");
        assertEquals("rootValue", firstElement.asText());
    }

    @Test
    void test_findNestedNod_2() {

        JsonNode firstElement = findNestedNode.findNestedNode(TestData.JSON_2, "root", "ele2");
        assertEquals("mary", firstElement.asText());
    }

    @Test
    void test_findNestedNod_3() {

        JsonNode firstElement = findNestedNode.findNestedNode(TestData.JSON_3, "root", "ele2", "ele3");
        assertEquals("zheng", firstElement.asText());
    }

    @Test
    void test_findNestedNod_4() {

        JsonNode firstElement = findNestedNode.findNestedNode(TestData.JSON_4, "root", "ele2", "ele3", "ele4");
        assertEquals("jcg", firstElement.asText());
    }

    @Test
    void test_notExist(){
        assertThrows(IllegalArgumentException.class, () ->
                findNestedNode.findNestedNode(TestData.JSON_4, "root", "bad"));
    }

    @Test
    void test_morefields(){
       JsonNode foundTest= findNestedNode.findNestedNode(TestData.JSON_5,"root", "ele2");
       assertTrue(foundTest.isContainerNode());
    }
}
  • Line 49: both findValue and path methods find the first node with the matching field name.

5.3 Path Nested Key Test

In this step, I will create a FindNodeByPathTest.java test that verifies finding a nested key via the path method. Note: this class is very similar to the FindNodeByValueTest class created in step 5.2.

FindNodeByPathTest.java

package org.example;

import com.fasterxml.jackson.databind.JsonNode;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;

class FindNodeByPathTest {

    FindNodeByPath findNestedNode = new FindNodeByPath();

    @Test
    void test_findNestedNode() {
        JsonNode firstElement = findNestedNode.findNestedNode(TestData.JSON_1, "root");
        assertEquals("rootValue", firstElement.asText());
    }

    @Test
    void test_findNestedNod_2() {
        JsonNode firstElement = findNestedNode.findNestedNode(TestData.JSON_2, "root", "ele2");
        assertEquals("mary", firstElement.asText());
    }

    @Test
    void test_findNestedNod_3() {
        JsonNode firstElement = findNestedNode.findNestedNode(TestData.JSON_3, "root", "ele2", "ele3");
        assertEquals("zheng", firstElement.asText());
    }

    @Test
    void test_findNestedNod_4() {
        JsonNode firstElement = findNestedNode.findNestedNode(TestData.JSON_4, "root", "ele2", "ele3", "ele4");
        assertEquals("jcg", firstElement.asText());
    }

    @Test
    void test_notExist(){
        assertThrows(IllegalArgumentException.class, () ->
        findNestedNode.findNestedNode(TestData.JSON_4, "root", "bad"));
    }

    @Test
    void test_morefields(){
        JsonNode foundTest= findNestedNode.findNestedNode(TestData.JSON_5,"root","ele2");
        assertTrue(foundTest.isContainerNode());
    }
}

Run both Junit tests and capture the output here.

findValue nested key
Figure 1 FindValue Tests

6. Conclusion

In this example, I created two classes to find a nested key from a JSON object utilizing JsonNode two methods: findValue() and path(). Both methods are very similar except handling of the not-found use case. The findValue method returns null while the path method returns a JsonNode with isMissingNode set to true.

7. Download

This was an example of a gradle project which included Jackson library to find a nested key in a JSON.

Download
You can download the full source code of this example here: Find Nested Key via Jackson Example

Mary Zheng

Mary graduated from the Mechanical Engineering department at ShangHai JiaoTong University. She also holds a Master degree in Computer Science from Webster University. During her studies she has been involved with a large number of projects ranging from programming and software engineering. She worked as a lead Software Engineer where she led and worked with others to design, implement, and monitor the software solution.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back to top button