Home > Mobile >  Can not cast deserialized json array with jackson
Can not cast deserialized json array with jackson

Time:08-02

I was working with jackson lib in java to deserialize an json file to an array list. First I use this method and everything worked fine.

ObjectMapper objectMapper = new ObjectMapper();
ArrayList<User> users = (ArrayList<User>) objectMapper.readValue(new File("data.json"), new TypeReference<List<User>>() {});

Then I decided to refactor the code and write a generic method to use for any type of data.

public static <T> ArrayList<T> listFromJson(String filename) throws IOException {
        ObjectMapper objectMapper = new ObjectMapper();
        return (ArrayList<T>) objectMapper.readValue(new File(filename), new TypeReference<List<T>>() {});
}

This method returns the array list without any exceptions. But when I want to use an element of arraylist and store it in a variable the program throws exception like this.

User user = users.get(0);
Exception in thread "main" java.lang.ClassCastException: class java.util.LinkedHashMap cannot be cast to class org.example.User ...
    ....

I also tried to print out the element without casting and it wasn't an object reference. It was something like a hashmap.

I think it is related to generics but I don't know the cause. Thanks for your help.

CodePudding user response:

Your object User is a JsonNode, something like this:

{
   "userName": "...",
   "email": "...",
   etc.
}

While this object can be mapped against a User.class if you specify so, when you don't say anything, the only guess that Jackson can take is that this is a Map<String, Object> (and the implementation a LinkedHashMap, meaning a map respecting its insertion order).

So when you refactored your method:

public static <T> ArrayList<T> listFromJson(String filename) throws IOException {
    ObjectMapper objectMapper = new ObjectMapper();
    return (ArrayList<T>) objectMapper.readValue(new File(filename), new TypeReference<List<T>>() {});
}

... you said to Jackson that it will find a List<T> where T can be any object. The only guess it can take is it will be a JsonNode (hence a Map.class).

To solve this issue, you should pass the concrete TypeReference to the method as well:

public static <T> ArrayList<T> listFromJson(String filename, TypeReference<?> expectedTypeReference) throws IOException {
    ObjectMapper objectMapper = new ObjectMapper();
    return (List<T>) objectMapper.readValue(new File(filename), expectedTypeReference);
}

... and use it like this:

TypeReference<List<User>> expectedTypeRef = new TypeReference<>() {};
List<User> users = listFromJson(fileName, expectedTypeRef);
User user = users.get(0); // <-- this should work
  • Related