This is a duplicate question because the following questions are either messy or they are not answered at all:
deserializing-a-generic-type-with-jackson
jack
JSON string that needs to be deserialized will have to contain the type information about parameter T.
You will have to put Jackson annotations on every class that can be passed as parameter T to class AgentResponse so that the type information about parameter type T can be read from / written to JSON string by Jackson.
Let us assume that T can be any class that extends abstract class Result.
public class AgentResponse<T extends Result> {
public Hits<T> hits;
}
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.WRAPPER_OBJECT)
@JsonSubTypes({
@JsonSubTypes.Type(value = ImageResult.class, name = "ImageResult"),
@JsonSubTypes.Type(value = NewsResult.class, name = "NewsResult")})
public abstract class Result {
}
public class ImageResult extends Result {
}
public class NewsResult extends Result {
}
Once each of the class (or their common supertype) that can be passed as parameter T is annotated, Jackson will include information about parameter T in the JSON. Such JSON can then be deserialized without knowing the parameter T at compile time.
This Jackson documentation link talks about Polymorphic Deserialization but is useful to refer to for this question as well.
public class AgentResponse<T> {
private T result;
public AgentResponse(T result) {
this.result = result;
}
public T getResult() {
return result;
}
}
So for the above class structure and T can be of Type T1,T2 class
So to deserialize for AgentResponse, use the following code
JavaType javaType = objectMapper.getTypeFactory.constructParametricType(AgentResponse.class,T1.class)
AgentResponse<T1> agentResponseT1 = objectMapper.readValue(inputJson,javaType);
You need to add some annotations on the constructor to tell Jackson how to build the object. The following worked for me:
public class AgentResponse<T> {
private T result;
@JsonCreator
public AgentResponse(@JsonProperty("result") T result) {
this.result = result;
}
public T getResult() {
return result;
}
}
Without the @JsonCreator annotation, Jackson cannot know to call this constructor. And without the @JsonProperty annotation, Jackson does not know that the first argument of the constructor maps to the result property.
I tried using the same approach but I haven't annotated my model class. It worked fine for me.
This is my model class
public class BasicMessage<T extends Serializable> implements Message<T> {
private MessageHeader messageHeader = new MessageHeader();
private T payload;
public MessageHeader getHeaders() {
return messageHeader;
}
public Object getHeader(String key) {
return messageHeader.get(key);
}
public Object addHeader(String key, Object header) {
return messageHeader.put(key, header);
}
public T getPayload() {
return payload;
}
public void setPayload(T messageBody) {
this.payload = messageBody;
}
}
And I used the following method for deserializing the payload
public static <T extends Serializable> BasicMessage<T> getConcreteMessageType(String jsonString, Class<T> classType) {
try {
ObjectMapper mapper = new ObjectMapper();
JavaType javaType = mapper.getTypeFactory().constructParametricType(BasicMessage.class, classType);
return mapper.readValue(jsonString, javaType);
} catch (IOException e) {
}
}
where jsonString contains the BasicMessageObject in a string.
If you programmatically pick up the java.lang.reflect.Type from for instance a method return type or a field, then it is easiest to use
Type type = ...;
ObjectMapper mapper = new ObjectMapper();
JavaType javaType = mapper.getTypeFactory().constructType( type );
Object value = mapper.readValue( json, javaType );
A fully nested JavaType is created, so Controller<PID<Temperature,Double>>> will be deserialzed correctly.