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 returnsnull
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 ofnull
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
returnsnull
.
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
withisMissingNode
astrue
.
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"}
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" } }
Here is the pretty formatted JSON_3. It has three levels of depth for a nested key: ele3
.
JSON_3
{ "root": { "ele2": { "ele3": "zheng" } } }
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" } } } }
Here is the pretty formatted JSON_5 which shows the ele2
shows at two different levels.
JSON_5
{ "root": { "ele2": { "test": { "ele2": "jcg" } } } }
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
andpath
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.
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.
You can download the full source code of this example here: Find Nested Key via Jackson Example