Search code examples
javagenericsjacksonjackson2

Why Jackson’s ObjectMapper methods take raw TypeReference


Jackson 2 ObjectMapper class defines numerous generic methods for deserializing JSON strings, byte arrays, files, etc. to given Java types.

Target type is given in an argument to those methods.

Eg. a type may be given as a Class<T>, like in the method

public <T> T readValue(String content, Class<T> valueType)

which returns the same T object (and thus is type-safe to use).

But it may also be given as a TypeReference<T> (which can encode a complex generic type), so one can build eg. new TypeReference<List<Long>> { } to tell ObjectMapper that the input needs to be deserialized into a list of longs – it can be passed eg. into the method:

@SuppressWarnings({ "unchecked", "rawtypes" })
public <T> T readValue(String content, TypeReference valueTypeRef)

But that method takes raw TypeReference and not a generic TypeReference<T>, and thus one needs to manually add a generic parameter to the call:

objectMapper.<List<Long>>readValue(input, listOfLongs)

and if one makes a mistake in the provided type, the compiler cannot catch that. That would not be a problem if the method signature was

public <T> T readValue(String content, TypeReference<T> valueTypeRef)

which would tell the compiler that the returned value is always of the same type as the generic parameter of provided TypeReference, similarly to how it works with Class<T>.

My question is – what is the reason behind such an API? Why do Jackson methods take raw TypeReference? Are there any valid cases when returned object is really of a different type that that referenced by generic parameter of TypeReference?

PS: What also puzzles me is that a corresponding convertValue method takes not a raw type, but a wildcard one:

public <T> T convertValue(Object fromValue, TypeReference<?> toValueTypeRef)

and similarly readValues:

public <T> MappingIterator<T> readValues(JsonParser p, TypeReference<?> valueTypeRef)

and the readValue(JsonParser, TypeReference) actually takes a fully qualified generic parameter:

public <T> T readValue(JsonParser p, TypeReference<T> valueTypeRef)

Solution

  • It is already reported as an issue, but was labeled as 3.x.

    There's a comment by the author of the library that explains why this hasn't been corrected yet:

    Looks like ObjectMapper API (and perhaps ObjectReader too) omits type-variable matching with TypeReference. This would make sense to change, but is likely to cause some source-compatibility issues (not binary), so perhaps do this in 3.0 and not earlier.