Converting Jackson JsonNode to Complex Java Collections

Snippet of programming code in IDE
Published on

Converting Jackson JsonNode to Complex Java Collections

In the world of modern application development, handling JSON data has become a fundamental skill, especially when working with APIs. One of the most powerful libraries to facilitate JSON parsing in Java is Jackson. Given its robustness, it allows us to convert JSON objects effortlessly into Java collections—be it simple lists, maps, or more complex nested structures.

In this post, we will explore how to convert Jackson's JsonNode to complex Java collections. We will cover scenarios such as converting to lists of objects, maps, and even collections within collections. Furthermore, we will also discuss the best practices to follow during this conversion process.

Understanding Jackson's JsonNode

Before we dive into the conversion process, it's essential to understand what a JsonNode is. JsonNode is a core component of the Jackson library used to represent a JSON structure.

You typically obtain a JsonNode by using the ObjectMapper class, as shown below:

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

public class JsonNodeExample {
    public static void main(String[] args) throws Exception {
        String json = "{ \"name\": \"John\", \"age\": 30 }";
        ObjectMapper mapper = new ObjectMapper();
        JsonNode node = mapper.readTree(json);

        System.out.println(node.get("name").asText()); // Prints: John
        System.out.println(node.get("age").asInt());   // Prints: 30
    }
}

This snippet highlights the ease of working with JsonNode but doesn't dive into collections just yet. The exciting part is how we can extract complex Java structures from this representation.

Converting JsonNode to Collections

Let's say you have a JSON object resembling the following:

{
    "employees": [
        {
            "name": "Alice",
            "age": 28,
            "projects": ["Project A", "Project B"]
        },
        {
            "name": "Bob",
            "age": 32,
            "projects": ["Project C"]
        }
    ]
}

Our objective is to extract this data into a list of custom objects. First, we will create a custom class that represents our employee.

Create a Custom Class

import java.util.List;

public class Employee {
    private String name;
    private int age;
    private List<String> projects;

    public Employee(String name, int age, List<String> projects) {
        this.name = name;
        this.age = age;
        this.projects = projects;
    }

    // Getters and toString() method for easier output
    public String getName() { return name; }
    public int getAge() { return age; }
    public List<String> getProjects() { return projects; }

    @Override
    public String toString() {
        return "Employee{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", projects=" + projects +
                '}';
    }
}

Converting JsonNode to List of Employees

Now, let's use Jackson to convert the JsonNode to a list of our Employee objects.

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.util.ArrayList;
import java.util.List;

public class JsonNodeToCollection {
    public static void main(String[] args) throws Exception {
        String json = "{ \"employees\": [{ \"name\": \"Alice\", \"age\": 28, \"projects\": [\"Project A\", \"Project B\"] }, { \"name\": \"Bob\", \"age\": 32, \"projects\": [\"Project C\"] }] }";
        ObjectMapper mapper = new ObjectMapper();
        JsonNode rootNode = mapper.readTree(json);
        JsonNode employeesNode = rootNode.path("employees");

        List<Employee> employees = new ArrayList<>();
        for (JsonNode employeeNode : employeesNode) {
            String name = employeeNode.path("name").asText();
            int age = employeeNode.path("age").asInt();
            List<String> projects = mapper.convertValue(employeeNode.path("projects"), List.class);
            employees.add(new Employee(name, age, projects));
        }

        // Output the extracted employees
        employees.forEach(System.out::println);
    }
}

Commentary on This Code

  • Path Method: The path method is used to navigate through the JsonNode. This approach avoids exceptions where the path does not exist by returning a “missing node.”

  • convertValue: The convertValue method allows seamless conversion from JsonNode to a List of Strings directly. It efficiently handles the transformation without the need for manual iteration.

Handling Nested Collections

JSON structures can often get more complicated, with additional layers of nesting. For instance, consider the following JSON structure that includes another collection.

{
    "departments": [
        {
            "name": "Development",
            "employees": [
                { "name": "Alice", "age": 28, "projects": ["Project A", "Project B"] },
                { "name": "Bob", "age": 32, "projects": ["Project C"] }
            ]
        },
        {
            "name": "Marketing",
            "employees": []
        }
    ]
}

To extract this information, we will need a new class that represents a department:

Create a Department Class

import java.util.List;

public class Department {
    private String name;
    private List<Employee> employees;

    public Department(String name, List<Employee> employees) {
        this.name = name;
        this.employees = employees;
    }

    // Getters and toString() method for easier output
    public String getName() { return name; }
    public List<Employee> getEmployees() { return employees; }

    @Override
    public String toString() {
        return "Department{" +
                "name='" + name + '\'' +
                ", employees=" + employees +
                '}';
    }
}

Converting to List of Departments

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.util.ArrayList;
import java.util.List;

public class JsonNodeNestedCollection {
    public static void main(String[] args) throws Exception {
        String json = "{ \"departments\": [{ \"name\": \"Development\", \"employees\": [{ \"name\": \"Alice\", \"age\": 28, \"projects\": [\"Project A\", \"Project B\"] }, { \"name\": \"Bob\", \"age\": 32, \"projects\": [\"Project C\"] }] }, { \"name\": \"Marketing\", \"employees\": [] }] }";
        ObjectMapper mapper = new ObjectMapper();
        JsonNode rootNode = mapper.readTree(json);
        JsonNode departmentsNode = rootNode.path("departments");

        List<Department> departments = new ArrayList<>();
        for (JsonNode departmentNode : departmentsNode) {
            String name = departmentNode.path("name").asText();
            List<Employee> employees = new ArrayList<>();
            for (JsonNode employeeNode : departmentNode.path("employees")) {
                String empName = employeeNode.path("name").asText();
                int age = employeeNode.path("age").asInt();
                List<String> projects = mapper.convertValue(employeeNode.path("projects"), List.class);
                employees.add(new Employee(empName, age, projects));
            }
            departments.add(new Department(name, employees));
        }

        // Output the extracted departments
        departments.forEach(System.out::println);
    }
}

The Bottom Line

Handling JSON data and converting it to Java collections using Jackson allows developers to interact seamlessly with external APIs and define custom structures based on the received data. This guide has provided you with essential techniques to convert JsonNode to complex Java collections effectively and efficiently.

For more detailed information about the features and benefits of Jackson, you can refer to the Jackson official documentation.

By mastering these skills, you will be well-equipped to tackle complex data transformations in Java, a crucial capability for any modern developer. Happy coding!