Reputation: 61
I'm trying to generalise this method:
public EventStream<Greeting> deserialize(String value){
ObjectMapper mapper = new ObjectMapper();
EventStream<Greeting> data = null;
try {
data = new ObjectMapper().readValue(value, new TypeReference<EventStream<Greeting>>() {});
} catch (IOException e) {
e.printStackTrace();
}
return data;
}
where EventStream is:
public class EventStream<T> {
private EventHeaders headers;
@JsonDeserialize
private T payload;
}
What I'd like to have is replace the specific Object Greeting with a generic, in the deserialize method.
I tried with this:
public <T> EventStream<T> deserialize(String value){
ObjectMapper mapper = new ObjectMapper();
EventStream<T> data = null;
try {
data = new ObjectMapper().readValue(value, new TypeReference<EventStream<T>>() {});
} catch (IOException e) {
e.printStackTrace();
}
return data;
}
But the payload inside the EventStream result is deserialized as LinkedHashMap. It seems like TypeReference ignored the generic type. Any idea? Thanks
Upvotes: 4
Views: 2070
Reputation: 1627
What you encountered here is a common problem caused by something called type erasure, the way java implements generics.
Type erasure can be explained as the process of enforcing type constraints only at compile time and discarding the element type information at runtime. [1]
So at the time you try to deserialize your object, the type T is not known and it is just treated as Object
and the deserialization result will default default to Map
(LinkedHashMap
to be precise).
You could make your method generic by passing your targetClass as an additional argument to the function call like so:
public <T> EventStream<T> deserialize(String value, Class<T> targetClass)
Then you use the TypeFactory of your mapper to create a type of this targetClass
JavaType type = mapper.getTypeFactory().constructParametricType(EventStream.class, targetClass);
which you can pass to the readValue method:
data = mapper.readValue(value, type);
Complete code:
public <T> EventStream<T> deserialize(String value, Class<T> targetClass){
ObjectMapper mapper = new ObjectMapper();
JavaType type = mapper.getTypeFactory()
.constructParametricType(EventStream.class, targetClass);
try {
return mapper.readValue(value, type);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
[1] https://www.baeldung.com/java-type-erasure
Upvotes: 1