Core Java

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 address field is a Map<String, Object>, which can store a variety of key-value pairs representing address details.
  • The phoneNumbers field is a List<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.

Download
You can download the full source code of this example here: Java jackson Jsonnode collection

Omozegie Aziegbe

Omos holds a Master degree in Information Engineering with Network Management from the Robert Gordon University, Aberdeen. Omos is currently a freelance web/application developer who is currently focused on developing Java enterprise applications with the Jakarta EE framework.
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