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