Search code examples
javajsonspring-bootjacksonjackson-databind

How to parse a JSON array of objects using Jackson?


I have a Json response as follows:

   {
      "data": {
        "date": "7 Apr 2022",
        "employee": [
          {
            "id": [
              "1288563656"
            ],
            "firstname": [
              "Mohammed"
            ],
            "lastname": [
              "Ali"
            ]
          }
        ]
      }
    }

I am trying to create a POJO called Employee and map it to the "employee" attribute in the JSON response.

This is what I did as an attempt:

Employee.java

public class Emoloyee {
    private Integer[] id;
    private String[] firstname;
    private String[] lastname;
    
    public Employee(Integer[] id, String[] firstname, String[] lastname){
        this.id = id;
        this.firstname = firstname;
        this.lastname = lastname;
    }
    
    public Employee(){
        
    }
    
    public Integet[] getId(){
        return id;
    }
    
    public void setId(Integer[] id){
        this.id = id;
    }
    
    public String[] getFirstname(){
        return firstname;
    }
    
    public void setFirstname(String[] firstname){
        this.firstname = firstname;
    }
    
    public String[] getLastname(){
        return lastname;
    }
    
    public void setLastname(String[] lastname){
        this.lastname = lastname;
    }
    
}

Using Jackson:

ObjectMapper mapper = new ObjectMapper();
URL jsonUrl = new URL("[API_URL]");
final ObjectNode node = mapper.readValue(jsonUrl, ObjectNode.class);
Employee[] employees = mapper.treeToValue(node.get("data").get("employee"), Employee[].class);

When I execute the app, I get the following error:

Caused by: com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize value of type 'long' from Array value (toke 'JsonToken.START_ARRAY')

As you may noticed, I am not interested of the date attribute, I only need to get the values of the employee and create an Employee object out of it.


Solution

  • The Employee POJO should look like this:

    @JsonProperty("id")
    private final List<String> ids;
    @JsonProperty("firstname")
    private final List<String> firstNames;
    @JsonProperty("lastname")
    private final List<String> lastNames;
    
    @JsonCreator
    public Employee(@JsonProperty(value = "id") List<String> ids, @JsonProperty(value = "firstname") List<String> firstNames, @JsonProperty(value = "lastname") List<String> lastNames) {
        this.ids = ids;
        this.firstNames = firstNames;
        this.lastNames = lastNames;
    }
    
    //getters code
    

    Then, you have an object Data:

    @JsonProperty("date")
    private final String date;
    @JsonProperty("employee")
    private final List<Employee> employees;
    
    @JsonCreator
    public Data(@JsonProperty(value = "date") String date, @JsonProperty(value = "employee") List<Employee> employees) {
        this.date = date;
        this.employees = employees;
    } 
    
    //getters code
    

    Finally, the whole Answer that you want to parse has this shape:

    @JsonProperty("data")
    private final Data data;
    
    @JsonCreator
    public Answer(@JsonProperty(value = "data") Data data) {
        this.data = data;
    } 
    
    //getter code
    

    Once you have defined these 3 classes, then you will be able to do:

    ObjectMapper objectMapper = new ObjectMapper();
    Answer answer = objectMapper.readValue(yourStringAnswer, Answer.class);
    

    Note: in your question, you are trying to parse an URL to an ObjectNode. I hardly doubt you would be able to do that. I guess you want to perform the HTTP request to the URL, then getting the response stream and that's what you want to parse into Answer (not the URL itself).

    Also, a few notes on the API response (in case you own it and so you can act on it):

    • All the lists would be more naturally declared with a plural name (e.g. employee should be employees)
    • The list of ids are numeric but are returned as strings. Also, why an employee would have a list of ids, and not a single id?
    • Why would an employee have a list of first names and last names? Shouldn't this be a simple string each (even if composed by more than one name)?
    • Use camel case (firstName, not firstname)
    • I don't see the point of putting everything into data, it may simply be the response containing date and employees