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 JsonNodefindValue(String fieldName)– finding the first JSON Object field with the specified name in this node or its child nodes. It returnsnullif 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 ofnullif 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
findValuemethod to find the field from JSON. - Line 21: when the field is not found,
findValuereturnsnull.
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
pathmethod to find the field. - Line 20: when the field is not found, it returns
JsonNodewithisMissingNodeastrue.
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
findValueandpathmethods 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





