Jackson JsonNode to Java Collections
Java applications frequently interact with data in JSON (JavaScript Object Notation) format. Jackson Java Library provides tools for working with JSON data. One key aspect is converting raw JSON data represented by the JsonNode class in Jackson to structured Java collections like Lists and Maps. This article explores various methods for transforming JsonNode objects into typed Java collections.
1. Example JSON Data
Prerequisites: Ensure you have the Jackson Library added to your project. If you’re using Maven, include the following dependencies:
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.17.1</version>
</dependency>
</dependencies>
Let’s use the following JSON data for the examples in this article:
{
"name": "Duke",
"age": 30,
"address": {
"street": "123 Main St",
"city": "Anytown"
},
"phoneNumbers": [
{
"type": "home",
"number": "555-555-5555"
},
{
"type": "work",
"number": "555-555-5556"
}
]
}
This JSON object represents a person’s profile with several details. Additionally, the phoneNumbers field is an array of objects, each representing different types of phone numbers.
2. Converting JsonNode to List and Map
2.1 Using ObjectMapper with TypeReference
This method involves using the ObjectMapper class with TypeReference to convert a JsonNode to a typed List or Map.
public class JsonNodeConversion {
public static void main(String[] args) throws FileNotFoundException {
// Create ObjectMapper instance
ObjectMapper mapper = new ObjectMapper();
try {
// Parse JSON file to JsonNode
JsonNode jsonNode = mapper.readTree(new FileReader("src/main/resources/data.json"));
// Convert JsonNode to Map
Map<String, Object> map = mapper.convertValue(jsonNode, new TypeReference<Map<String, Object>>() {
});
System.out.println("Map: " + map);
// Convert JsonNode to List
JsonNode phoneNumbersNode = jsonNode.get("phoneNumbers");
List<Map<String, Object>> list = mapper.convertValue(phoneNumbersNode, new TypeReference<List<Map<String, Object>>>() {
});
System.out.println("List: " + list);
} catch (IOException e) {
System.out.println(" "+ e);
}
}
}
In this method, we use the ObjectMapper class along with TypeReference to convert a JsonNode to a typed List or Map. The TypeReference class allows us to specify the target type in a generic way.
Here we read a JSON string into a JsonNode and then convert it into a Map<String, Object> and a List<Map<String, Object>>. This method is straightforward and leverages Jackson’s type handling to automatically convert the JsonNode to the desired types.
2.2 Using ObjectMapper with readValue
Another method is to use the readValue method of ObjectMapper to convert a JsonNode to a typed List or Map.
// Convert JsonNode to Map
Map<String, Object> map = mapper.readValue(jsonNode.toString(), new TypeReference<Map<String, Object>>() {
});
System.out.println("Map: " + map);
// Convert JsonNode to List
JsonNode phoneNumbersNode = jsonNode.get("phoneNumbers");
List<Map<String, Object>> list = mapper.readValue(phoneNumbersNode.traverse(), new TypeReference<List<Map<String, Object>>>() {
});
System.out.println("List: " + list);
This method involves using the readValue method of ObjectMapper to directly read the JSON string and convert it into a typed collection.
2.3 Manually Traversing JsonNode
We can manually traverse the JsonNode to convert it into a typed List or Map.
public class ManualJsonNodeConversion {
public static void main(String[] args) {
ObjectMapper mapper = new ObjectMapper();
try {
JsonNode jsonNode = mapper.readTree(new FileReader("src/main/resources/data.json"));
// Convert JsonNode to Map
Map<String, Object> map = new HashMap<>();
Iterator<Map.Entry<String, JsonNode>> fields = jsonNode.fields();
while (fields.hasNext()) {
Map.Entry<String, JsonNode> field = fields.next();
map.put(field.getKey(), field.getValue());
}
System.out.println("Map: " + map);
// Convert JsonNode to List
JsonNode phoneNumbersNode = jsonNode.get("phoneNumbers");
List<Map<String, Object>> list = new ArrayList<>();
if (phoneNumbersNode.isArray()) {
for (JsonNode node : phoneNumbersNode) {
Map<String, Object> phoneNumberMap = new HashMap<>();
node.fields().forEachRemaining(entry -> phoneNumberMap.put(entry.getKey(), entry.getValue()));
list.add(phoneNumberMap);
}
}
System.out.println("List: " + list);
} catch (IOException e) {
System.out.println(" " + e);
}
}
}
In this method, we manually traverse the JsonNode to convert it into a typed List or Map. This approach involves iterating over the fields of the JsonNode and populating a Map or List manually. While this method is more verbose, it provides complete control over the conversion process.
3. Using Custom Deserialization
We can create custom deserializers for more complex scenarios. Create a Person class and ensure it has getters, setters, and a default constructor.
Define the Person Class
public class Person {
public String name;
public int age;
@JsonDeserialize(using = AddressDeserializer.class)
public Map<String, Object> address;
@JsonDeserialize(using = PhoneNumbersDeserializer.class)
public List<Map<String, Object>> phoneNumbers;
public Person() {
}
public Person(String name, int age, Map<String, Object> address, List<Map<String, Object>> phoneNumbers) {
this.name = name;
this.age = age;
this.address = address;
this.phoneNumbers = phoneNumbers;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Map<String, Object> getAddress() {
return address;
}
public void setAddress(Map<String, Object> address) {
this.address = address;
}
public List<Map<String, Object>> getPhoneNumbers() {
return phoneNumbers;
}
public void setPhoneNumbers(List<Map<String, Object>> phoneNumbers) {
this.phoneNumbers = phoneNumbers;
}
}
The Person class is a Java bean that represents a person with four fields: name, age, address, and phoneNumbers.
- The
addressfield is aMap<String, Object>, which can store a variety of key-value pairs representing address details. - The
phoneNumbersfield is aList<Map<String, Object>>, allowing for a list of maps, each representing a phone number’s details. - The class includes a no-argument constructor, a parameterized constructor, and standard getters and setters for all fields.
3.1 Create the Custom Deserializers
We need to create a custom deserializer that extends JsonDeserializer and implement the deserialize method to parse the JsonNode.
Create AddressDeserializer.java: Create a custom deserializer for the address field.
public class AddressDeserializer extends JsonDeserializer<Map<String, Object>> {
@Override
public Map<String, Object> deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
Map<String, Object> addressMap = new HashMap<>();
ObjectMapper mapper = (ObjectMapper) p.getCodec();
JsonNode node = mapper.readTree(p);
node.fields().forEachRemaining(field -> addressMap.put(field.getKey(), field.getValue().asText()));
return addressMap;
}
}
The AddressDeserializer class extends JsonDeserializer<Map<String, Object>> and overrides the deserialize method to provide custom deserialization logic for the address field. This method uses an ObjectMapper to read the JSON node and iterates over the fields of the address node using the forEachRemaining method. It populates a HashMap with the address details, converting each field’s value to a string.
Create a custom deserializer for the phoneNumbers field.
public class PhoneNumbersDeserializer extends JsonDeserializer<List<Map<String, Object>>> {
@Override
public List<Map<String, Object>> deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
List<Map<String, Object>> phoneNumbersList = new ArrayList<>();
ObjectMapper mapper = (ObjectMapper) p.getCodec();
JsonNode node = mapper.readTree(p);
node.forEach(phoneNumberNode -> {
Map<String, Object> phoneNumberMap = new HashMap<>();
phoneNumberNode.fields().forEachRemaining(field -> phoneNumberMap.put(field.getKey(), field.getValue().asText()));
phoneNumbersList.add(phoneNumberMap);
});
return phoneNumbersList;
}
}
The PhoneNumbersDeserializer class extends JsonDeserializer<List<Map<String, Object>>> and overrides the deserialize method to provide custom deserialization logic for the phoneNumbers field. It uses an ObjectMapper to read the JSON node and iterates over the elements of the phoneNumbers array using the forEach method.
For each phone number node, it iterates over the fields and populates a HashMap with the phone number details. These maps are then added to a List<Map<String, Object>>, ensuring that the phoneNumbers field in the JSON is correctly mapped to a list of maps in the Person class.
These custom deserializers ensure that the nested JSON fields will be correctly mapped to the corresponding Java data structures
Register the Custom Deserializer
To use the custom deserializers, register them with the ObjectMapper.
public class CustomDeserializationExample {
public static void main(String[] args) {
ObjectMapper mapper = new ObjectMapper();
try {
JsonNode jsonNode = mapper.readTree(new FileReader("src/main/resources/data.json"));
SimpleModule module = new SimpleModule();
module.addDeserializer(Map.class, new AddressDeserializer());
module.addDeserializer(List.class, new PhoneNumbersDeserializer());
mapper.registerModule(module);
Person person = mapper.readValue(jsonNode.toString(), Person.class);
System.out.println("Name: " + person.getName());
System.out.println("Age: " + person.getAge());
System.out.println("Address: " + person.getAddress());
System.out.println("Phone Numbers: " + person.getPhoneNumbers());
} catch (IOException e) {
System.out.println(" " + e);
}
}
}
Here, we initialize an ObjectMapper and set up a SimpleModule to register AddressDeserializer and PhoneNumbersDeserializer. These custom deserializers handle the nested address and phoneNumbers fields. The module is registered with the ObjectMapper, which then converts the JsonNode to a Person object.
Test the Deserialization
Run the Main class. The output should display the deserialized Person object, including the address and phone numbers.
Output
4. Conclusion
In this article, we explored various methods for converting Jackson JsonNode objects into typed Java collections. We discussed using ObjectMapper with TypeReference and readValue methods to transform JSON data into List and Map structures. Additionally, we demonstrated how to manually traverse JsonNode and employ custom deserializers for more complex scenarios. By utilizing these techniques, we can efficiently manage and manipulate JSON data in Java applications, leveraging the capabilities of the Jackson library.
5. Download the Source Code
This article explores how to convert Jackson JsonNode objects to Java collection type.
You can download the full source code of this example here: Java jackson Jsonnode collection


