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
jackson-deserialize-into-runtime-specified-class
jackson-deserialize-using-generic-class
jackson-deserialize-generic-class-variable
I hope that this question will finally find an answer that makes this clear for good.
Having a model :
public class AgentResponse<T> { private T result; public AgentResponse(T result) { this.result = result; } public T getResult() { return result; } }
JSON input:
{"result":{"first-client-id":3,"test-mail-module":3,"third-client-id":3,"second-client-id":3}}
and two recommended ways of deserializing generic types :
mapper.readValue(out, new TypeReference<AgentResponse<Map<String, Integer>>>() {});
or
JavaType javaType = mapper.getTypeFactory().constructParametricType(AgentResponse.class, Map.class); mapper.readValue(out, javaType);
Jackson is never able to deal with the generic type T, it figures it's a Map from JavaType, but it finds Object type constructor argument because of type erasure and throws an error. So is this a Jackson bug, or am I doing something wrong? What else is explicit specification of TypeReference or JavaType for?
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)
The Jackson ObjectMapper can parse JSON from a string, stream or file, and create a Java object or object graph representing the parsed JSON. Parsing JSON into Java objects is also referred to as to deserialize Java objects from JSON. The Jackson ObjectMapper can also create JSON from Java objects.
A JsonNode is Jackson's tree model for JSON and it can read JSON into a JsonNode instance and write a JsonNode out to JSON. To read JSON into a JsonNode with Jackson by creating ObjectMapper instance and call the readValue() method. We can access a field, array or nested object using the get() method of JsonNode class.
Jackson uses default (no argument) constructor to create object and then sets value using setters. so you only need @NoArgsConstructor and @Setter.
Class TypeReference<T>This generic abstract class is used for obtaining full generics type information by sub-classing; it must be converted to ResolvedType implementation (implemented by JavaType from "databind" bundle) to be used.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With