java generics jackson deserialization

java - ¿Jackson realmente no puede deserializar a json en un tipo genérico?



generics deserialization (3)

Intenté usar el mismo enfoque, pero no anoté mi clase de modelo. Funcionó bien para mí

Esta es mi clase de modelo

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; } }

Y usé el siguiente método para deserializar la carga útil

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) { } }

donde jsonString contiene BasicMessageObject en una cadena.

Esta es una pregunta duplicada porque las siguientes preguntas son confusas o no se responden en absoluto:

deserializing-a-generic-type-with-jackson

jackson-deserialize-into-runtime-specified-class

jackson-deserialize-using-generic-class

jackson-deserialize-generic-class-variable

Espero que esta pregunta finalmente encuentre una respuesta que lo aclare para siempre.

Tener un modelo:

public class AgentResponse<T> { private T result; public AgentResponse(T result) { this.result = result; } public T getResult() { return result; } }

Entrada JSON:

{"result":{"first-client-id":3,"test-mail-module":3,"third-client-id":3,"second-client-id":3}}

y dos formas recomendadas de deserializar tipos genéricos:

mapper.readValue(out, new TypeReference<AgentResponse<Map<String, Integer>>>() {});

o

JavaType javaType = mapper.getTypeFactory().constructParametricType(AgentResponse.class, Map.class); mapper.readValue(out, javaType);

Jackson nunca puede tratar con el tipo genérico T, cree que es un Mapa de JavaType, pero encuentra un argumento de constructor de tipo Objeto debido a la borradura de tipo y arroja un error. Entonces, ¿es esto un error de Jackson, o estoy haciendo algo mal? ¿Qué más es la especificación explícita de TypeReference o JavaType para?

com.fasterxml.jackson.databind.JsonMappingException: No suitable constructor found for type [simple type, class com.fg.mail.smtp.AgentResponse<java.util.Map<java.lang.String,java.lang.Integer>>]: can not instantiate from JSON object (need to add/enable type information?) at [Source: java.io.InputStreamReader@4f2d26d; line: 1, column: 2] at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:164) at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:984) at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:276) at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:121) at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:2888) at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2064)


La cadena JSON que necesita ser deserializada deberá contener la información de tipo sobre el parámetro T
Deberá colocar anotaciones de Jackson en cada clase que se pueda pasar como parámetro T para clasificar AgentResponse modo que la información de tipo sobre el tipo de parámetro T pueda leerse / escribirse en cadena JSON por Jackson.

Supongamos que T puede ser cualquier clase que extienda la clase abstracta 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 { }

Una vez que cada clase (o su supertipo común) que se puede pasar como parámetro T se anota, Jackson incluirá información sobre el parámetro T en el JSON. Tal JSON puede deserializarse sin conocer el parámetro T en el momento de la compilación.
Este enlace de documentación de Jackson habla de deserialización polimórfica, pero también es útil referirse a esta pregunta.


Necesita agregar algunas anotaciones en el constructor para decirle a Jackson cómo construir el objeto. Lo siguiente funcionó para mí:

public class AgentResponse<T> { private T result; @JsonCreator public AgentResponse(@JsonProperty("result") T result) { this.result = result; } public T getResult() { return result; } }

Sin la anotación @JsonCreator , Jackson no puede saber llamar a este constructor. Y sin la anotación @JsonProperty , Jackson no sabe que el primer argumento del constructor se correlaciona con la propiedad del result .