Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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) 
like image 438
lisak Avatar asked Jul 01 '13 09:07

lisak


People also ask

How Jackson JSON works?

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.

How does Jackson read nested JSON?

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.

Does Jackson require default constructor?

Jackson uses default (no argument) constructor to create object and then sets value using setters. so you only need @NoArgsConstructor and @Setter.

What is Jackson's TypeReference?

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.


1 Answers

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.

like image 87
cambecc Avatar answered Oct 13 '22 17:10

cambecc