Search code examples
javajsonjacksondeserializationjackson-databind

TypeReference created from Generic type does not work as intended


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?


Solution

  • 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.