Is Jackson really unable to deserialize json into a generic type?

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 = obj.getClass().getDeclaredField( "aKnownFieldName" ).getGenericType(); 
// or
Type type = obj.getClass().getDeclaredMethod( "aKnownMethod" ).getGenericReturnType(); 


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.


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.