I am trying to parse a JSON string into a Map<String, Map<Long, Long>>
. I had a method that performed serialization using Jackson's ObjectMapper
by constructing a TypeReference
using a generic type, but this did not serialize the object correctly.
The method I was using looked like to serialize the string into Map<String, Map<Long, Long> is this:
public static <T> T to(String jsonString) {
TypeReference<T> typeReference = new TypeReference<T>() {};
return WrapThrowingSupplier.wrapIOException(() -> new ObjectMapper().readValue(jsonString, typeReference));
}
Which I called as follows:
Map<String, Map<Long, Long>> map = to(jsonString);
However, this did not work, and trying to iterate through the inner map's keyset like:
innerMap.forEach(entry -> //do things with long key);
threw ClassCastException: class java.lang.String cannot be cast to class java.lang.Long
.
I'm guessing that the String
keys in the JSON could not be converted to Long
in the inner map.
However, I tried doing this after looking at another SO answer:
Map<String, Map<Long, Long>> map2 = new ObjectMapper().readValue(jsonString, new TypeReference<>() {});
which works as expected. What is the difference between these two approaches?
Your to
method doesn’t know what T
is and the TypeReference
class has no way to create information that doesn’t exist. All it carries to the JSON deserializer, is “T
”.
In contrast, your statement
Map<String, Map<Long, Long>> map2 = new ObjectMapper()
.readValue(jsonString, new TypeReference<>() {});
does know that the intended target type is Map<String, Map<Long, Long>>
and the compiler infers that you are creating a new TypeReference<Map<String, Map<Long, Long>>>() {}
(this works since Java 9).
In other words, the purpose of the TypeReference
is to carry the type information from a code location where the actual type is known through the generic methods. It does not work when you create the TypeReference
in a generic method where the type information is already lost.