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

后端 未结 5 879
独厮守ぢ
独厮守ぢ 2020-11-30 01:35

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

相关标签:
5条回答
  • 2020-11-30 02:08

    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.

    0 讨论(0)
  • 2020-11-30 02:09
    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);
    
    0 讨论(0)
  • 2020-11-30 02:11

    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.

    0 讨论(0)
  • 2020-11-30 02:28

    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.

    0 讨论(0)
  • 2020-11-30 02:30

    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.

    0 讨论(0)
提交回复
热议问题