When working with JSON, the most common approach is to bind JSON data directly to Java POJOs (Plain Old Java Objects) using Jackson’s data binding feature. However, sometimes the structure of the incoming JSON is dynamic, unknown, or too flexible to predefine Java classes.
In these cases, Jackson’s Tree Model API comes in handy by allowing us to parse JSON into a tree structure that can be traversed dynamically.
1.What Is the Tree Model (JsonNode)?
- The Tree Model allows us to parse JSON into a tree of
JsonNode
objects. - Each
JsonNode
represents a part of the JSON data:- ObjectNode → Represents a JSON object
{ ... }
. - ArrayNode → Represents a JSON array
[ ... ]
. - ValueNode → Represents JSON values (strings, numbers, booleans, null).
- ObjectNode → Represents a JSON object
This approach treats the JSON structure like a tree, allowing traversal, dynamic access, and even modification of individual nodes.
2. Why Use the Tree Model?
- 👉 Useful when the JSON structure is unpredictable or changes frequently.
- 👉 Allows selective data extraction without having to map the entire JSON to a predefined class.
- 👉 Facilitates reading deeply nested properties or arrays in a flexible manner.
- 👉 Supports modifications before re-serializing the data back to JSON.
3. How to Parse JSON into Tree Model using JsonNode
✔️ Example JSON:
{
"name": "Ashish Kumar",
"age": 30,
"address": {
"city": "Delhi",
"zipcode": "110001"
},
"skills": ["Java", "Spring Boot", "Jackson"]
}
✔️Example Java Code:
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
public class JacksonTreeModelExample {
public static void main(String[] args) {
try {
String json = "{ \"name\": \"Ashish Kumar\", \"age\": 30, \"address\": { \"city\": \"Delhi\", \"zipcode\": \"110001\" }, \"skills\": [\"Java\", \"Spring Boot\", \"Jackson\"] }";
ObjectMapper objectMapper = new ObjectMapper();
// Parse JSON into a tree of JsonNode
JsonNode rootNode = objectMapper.readTree(json);
// Access simple properties
String name = rootNode.get("name").asText();
int age = rootNode.get("age").asInt();
System.out.println("Name: " + name);
System.out.println("Age: " + age);
// Access nested object
JsonNode addressNode = rootNode.get("address");
String city = addressNode.get("city").asText();
String zipcode = addressNode.get("zipcode").asText();
System.out.println("City: " + city);
System.out.println("Zipcode: " + zipcode);
// Access array
JsonNode skillsNode = rootNode.get("skills");
System.out.print("Skills: ");
for (JsonNode skill: skillsNode) {
System.out.print(skill.asText() + ",");
}
} catch(Exception e) {
e.printStackTrace();
}
}
}
✔️ Expected Output:
Name: Ashish Kumar
Age: 30
City: Delhi
Zipcode: 110001
Skills: Java Spring Boot Jackson
✅ Detailed Explanation of the Process
- ✔ Parsing JSON:
objectMapper.readTree(json)
reads the entire JSON string and creates a tree ofJsonNode
objects.rootNode
represents the root of the tree.
- ✔ Accessing Simple Properties:
rootNode.get("name").asText()
extracts the"name"
property as a String.rootNode.get("age").asInt()
extracts the"age"
property as an integer.
- ✔ Accessing a Nested Object:
rootNode.get("address")
returns aJsonNode
representing the"address"
object.- Then we extract individual fields from
addressNode
:addressNode.get("city").asText()
addressNode.get("zipcode").asText()
- ✔ Accessing Array Elements:
rootNode.get("skills")
returns aJsonNode
representing the JSON array.- Iterating through the array elements using a
for-each
loop and accessing each skill’s value withskill.asText()
.
✅ Advantages of Tree Model
Advantage | Explanation |
---|---|
Dynamic Parsing | No need to create Java classes in advance. Useful for unknown or changing JSON structures. |
Selective Data Access | Access only the parts of the JSON you need, saving memory and effort. |
Nested and Array Support | Easily traverse deeply nested objects and arrays without mapping everything upfront. |
✅ When to Use Tree Model vs Data Binding
Scenario | Recommended Approach |
---|---|
Static and well-defined structure | Use POJOs + ObjectMapper (Serialization/Deserialization) |
Unknown, dynamic, or partial structure | Use Tree Model (JsonNode ) |
Need to modify part of the JSON | Use ObjectNode (which extends JsonNode ) to manipulate fields |
4. Creating JSON Objects Dynamically with ObjectNode
In some scenarios, you don’t have a predefined Java object (POJO) to represent the JSON you want to generate.
For example, when building dynamic JSON responses in REST APIs, or transforming data on the fly.
Jackson’s ObjectNode
class is ideal for such cases.
✅ What Is ObjectNode?
ObjectNode
is a subclass ofJsonNode
that represents a JSON object{ ... }
.- Allows you to dynamically create, modify, and remove fields without needing a POJO.
- Ideal when you want to construct a JSON object programmatically
Example :
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
public class DynamicJsonExample {
public static void main(String[] args) throws Exception {
ObjectMapper objectMapper = new ObjectMapper();
// Create the root JSON object
ObjectNode jsonObject = objectMapper.createObjectNode();
// Add simple fields
jsonObject.put("name", "Ashish Kumar");
jsonObject.put("age", 30);
// Add nested address object
ObjectNode addressNode = objectMapper.createObjectNode();
addressNode.put("city", "Delhi");
addressNode.put("zipcode", "110001");
jsonObject.set("address", addressNode);
// Add skills array
ArrayNode skillsArray = objectMapper.createArrayNode();
skillsArray.add("Java");
skillsArray.add("Spring Boot");
skillsArray.add("Jackson");
jsonObject.set("skills", skillsArray);
// Output the pretty-printed JSON string
System.out.println(jsonObject.toPrettyString());
}
}
Explanation :
Output :
{
"name" : "Ashish Kumar",
"age" : 30,
"address" : {
"city" : "Delhi",
"zipcode" : "110001"
},
"skills" : [ "Java", "Spring Boot", "Jackson" ]
}
📌Why This Example Is Useful
- ✅ Demonstrates how to create simple fields using
put()
. - ✅ Shows how to add a nested object with
ObjectNode
. - ✅ Explains how to build an array field using
ArrayNode
. - ✅ Illustrates use cases like building dynamic JSON responses in REST APIs, building configuration files dynamically, or performing data transformations.
5. Working with JSON Arrays in Java
A JSON Array is an ordered collection of values, which may include objects, strings, numbers, booleans, or other primitives. In Java, using Jackson, you can easily parse and construct JSON arrays with the help of the ArrayNode
class, which extends JsonNode
.
✔️ Example JSON Array
[
{"productId": 101, "productName": "Wireless Mouse", "price": 25.99},
{"productId": 102, "productName": "Mechanical Keyboard", "price": 75.49}
]
5.1 Parsing JSON Arrays with Jackson
Imagine you receive a JSON array from an external API that lists products available in an online store. You can parse the JSON array string into an ArrayNode
and iterate through its elements like this:
String jsonArrayString = "[" +
"{\"productId\": 101, \"productName\": \"Wireless Mouse\", \"price\": 1899.00}," +
"{\"productId\": 102, \"productName\": \"Mechanical Keyboard\", \"price\": 5499.00}" +
"]";
ArrayNode jsonArray = (ArrayNode) objectMapper.readTree(jsonArrayString);
for (JsonNode product : jsonArray) {
System.out.println("Product ID: " + product.get("productId").asInt());
System.out.println("Product Name: " + product.get("productName").asText());
System.out.println("Price: ₹" + product.get("price").asDouble());
System.out.println("-----------------------------------");
}
✔️ Example output:
Product ID: 101
Product Name: Wireless Mouse
Price: ₹1899.0
-----------------------------------
Product ID: 102
Product Name: Mechanical Keyboard
Price: ₹5499.0
-----------------------------------
5.2 Constructing JSON Arrays Dynamically
Suppose your application needs to dynamically build a product catalog before returning it as a JSON response. You can use ArrayNode
to construct the array programmatically:
ArrayNode productsArray = objectMapper.createArrayNode();
ObjectNode product1 = objectMapper.createObjectNode()
.put("productId", 201)
.put("productName", "USB-C Charger")
.put("price", 999.00);
ObjectNode product2 = objectMapper.createObjectNode()
.put("productId", 202)
.put("productName", "Noise Cancelling Headphones")
.put("price", 7499.00);
productsArray.add(product1);
productsArray.add(product2);
System.out.println(productsArray.toString());
✔ Example output:
[
{"productId":201,"productName":"USB-C Charger","price":999.0},
{"productId":202,"productName":"Noise Cancelling Headphones","price":7499.0}
]
👉 In a real-time e-commerce system, this dynamic creation of product lists allows you to build flexible APIs that respond with the most up-to-date product information in INR.
6. Choosing Between ArrayNode
and JsonNode
When working with JSON data using Jackson, knowing when to use ArrayNode
versus JsonNode
helps you write cleaner, more efficient, and maintainable code.
✅ Use ArrayNode
when:
- You are working with a JSON structure where you know the data is always an array.
- You need to perform specific operations on array elements, such as filtering, sorting, or mapping.
- The structure is well-defined, and you want to work directly with array-specific methods (like
.add()
,.remove()
,.size()
, etc.).
✅ Use JsonNode
when:
- You are dealing with dynamic or nested JSON structures, where some fields could be arrays, objects, or primitive values.
- You need a flexible and generic approach to process JSON without assuming a strict structure.
- You want to parse arbitrary JSON data and explore it at runtime, especially when the schema is not predefined.
👉 Choosing the right type between ArrayNode
and JsonNode
helps improve code clarity and performance in your application.
7. Difference Between get()
and path()
in Jackson Tree Model
When working with the Jackson Tree Model (JsonNode), you often need to access specific fields or nested data in a JSON structure. Two commonly used methods for this are .get()
and .path()
. Understanding their differences helps you write more robust and error-resistant code.
7.1 get(String fieldName)
- Behavior: Returns the value of the specified field as a
JsonNode
. - If the field does not exist: It returns
null
. - Use Case: When you are sure the field exists and want to get the exact node.
Example:
JsonNode nameNode = rootNode.get("name"); // returns null if "name" field is missing
⚠️ Risk: If you immediately call .asText()
or .asInt()
after .get()
, and the field does not exist (i.e., null
is returned), you will get a NullPointerException
.
7.2 path(String fieldName)
- Behavior: Returns the value of the specified field as a
JsonNode
. - If the field does not exist: Returns a missing node (
MissingNode
), which is a specialJsonNode
that does not throwNullPointerException
and safely returns default values.
Example:
JsonNode nameNode = rootNode.path("name"); // Returns MissingNode if "name" does not exist
String name = nameNode.asText(); // Returns "" (empty string) if missing
⚡ Advantages of path()
- ✔ Safe Access Without NullPointerException
Even if the field doesn’t exist,path()
ensures that your code doesn’t throw aNullPointerException
. Instead, it returns aMissingNode
, and calling.asText()
,.asInt()
, etc., returns sensible defaults:.asText()
→""
.asInt()
→0
- ✔ More Robust for Dynamic or Uncertain JSON Structures
In real-world applications where the JSON structure might change or fields may be missing, using.path()
prevents your code from breaking unexpectedly.
✅ Example Comparison
// Using get() – Potential NullPointerException
String name = rootNode.get("name").asText(); // Works if "name" exists
String phone = rootNode.get("phone").asText(); // Throws NullPointerException if "phone" is missing
// Using path() – Safe even if field missing
String name = rootNode.path("name").asText(); // Returns "Ashish Kumar"
String phone = rootNode.path("phone").asText(); // Returns "" (empty string)
Note :
📦 When Was path()
Introduced?
The path()
method has been available since early versions of Jackson (introduced around Jackson 2.x series) and has become a standard best practice for safely accessing JSON tree nodes when the presence of fields is not guaranteed.
🎯 Conclusion
Jackson’s Tree Model API is a powerful and flexible approach for handling JSON data in Java, especially when working with dynamic, unknown, or partially structured JSON. Unlike the traditional POJO-based data binding approach, the Tree Model provides the ability to parse JSON into a tree of JsonNode
objects, enabling dynamic traversal, selective data access, and easy manipulation of JSON content.
By using ObjectNode
and ArrayNode
, developers can dynamically construct JSON objects and arrays programmatically, making it ideal for use cases such as building REST API responses or handling configuration files. Furthermore, the choice between .get()
and .path()
methods ensures safer and more robust handling of potentially missing fields in JSON data.
In summary, when dealing with unpredictable or frequently changing JSON structures, leveraging the Tree Model approach offers maximum flexibility, reduces boilerplate code, and improves the maintainability of your Java applications. Always prefer path()
over get()
in uncertain scenarios to avoid NullPointerException
and ensure more stable code execution.