Search code examples
javajacksonspring-bootdeserializationresttemplate

Java: Deserialize a json to object in rest template using "@class" in json -SpringBoot


I have to instantiate a class which extends the abstract class from JSON using information in @class as shown below.

"name": {
  "health": "xxx",
  "animal": {
    "_class": "com.example.Dog",
    "height" : "20"
    "color" : "white"
  }
},

Here the abstract class is animal and dog extends the animal class. So using the information in @class, can we instantiate dog directly. Also this is the response I am getting in restTemplate

ResponseEntity<List<SomeListName>> response = restTemplate.exchange("http://10.150.15.172:8080/agencies", HttpMethod.GET, request, responseType);

The following error is coming when this line is executed. Since the POJO classes are auto-generated, I cannot use annotations like @JsonTypeInfo

I am using Spring boot and maven. This error is coming in console.

Could not read JSON: Can not construct instance of "MyPOJO", problem: abstract types either need to be mapped to concrete types, have custom deserializer, or be instantiated with additional type information


Solution

  • You can use @JsonTypeInfo annotation regardless of the fact that the classes are generated, by adhering to MixIn's

    "mix-in annotations are": a way to associate annotations with classes, without modifying (target) classes themselves.

    That is, you can:

    Define that annotations of a mix-in class (or interface) will be used with a target class (or interface) such that it appears as if the target class had all annotations that the mix-in class has (for purposes of configuring serialization / deserialization)

    So you can write your AnimalMixIn class, something like

    @JsonTypeInfo(  
        use = JsonTypeInfo.Id.NAME,  
        include = JsonTypeInfo.As.PROPERTY,  
        property = "_class")  
    @JsonSubTypes({  
        @Type(value = Cat.class, name = "com.example.Cat"),  
        @Type(value = Dog.class, name = "com.example.Dog") })  
    abstract class AnimalMixIn  
    {  
    
    }  
    

    and configure your deserializer

        ObjectMapper mapper = new ObjectMapper();  
        mapper.getDeserializationConfig().addMixInAnnotations(  
        Animal.class, AnimalMixIn.class);
    

    Since you're using Spring Boot, you can check the following blog post to see how you can customize the ObjectMapper for using the MixIns, Customizing the Jackson Object Mapper, note especially the mixIn method of Jackson2ObjectMapperBuilder

    Using customized ObjectMapper within RestTemplate should be set through converters, something like

        RestTemplate restTemplate = new RestTemplate();
        List<HttpMessageConverter<?>> messageConverters = new ArrayList<>();
        MappingJackson2HttpMessageConverter jsonMessageConverter = new MappingJackson2HttpMessageConverter();
        jsonMessageConverter.setObjectMapper(objectMapper);
        messageConverters.add(jsonMessageConverter);
        restTemplate.setMessageConverters(messageConverters);
        return restTemplate;