4

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

2 Answers 2

8

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;
Sign up to request clarification or add additional context in comments.

2 Comments

Thank you very much for the answer. But since i am using spring boot, Do I have to use any custom object mappers which will replace HttpMessageConverter's default object mapper in restTemplate.
You welcome, yes, you'll have to customize the default object mapper, I've provided some references in the answer
1

Just as an addition to the answer from @Master Slave. He said

you can customize the ObjectMapper for using the MixIns, Customizing the Jackson Object Mapper, note especially the mixIn method of Jackson2ObjectMapperBuilder

but he didn't define the ObjectMapper for the RestTemplate. Here is how his answer helped me to figure out my complete answer.

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.addMixIn(Target.class, MixinClass.class)
    .setVisibility(VisibilityChecker.Std.defaultInstance()
        .withFieldVisibility(JsonAutoDetect.Visibility.ANY)
        .withGetterVisibility(JsonAutoDetect.Visibility.ANY)
        .withSetterVisibility(JsonAutoDetect.Visibility.ANY)
        .withCreatorVisibility(JsonAutoDetect.Visibility.ANY)
    );
List<HttpMessageConverter<?>> messageConverters = new ArrayList<>();
MappingJackson2HttpMessageConverter jsonMessageConverter = new MappingJackson2HttpMessageConverter();
jsonMessageConverter.setObjectMapper(objectMapper);
messageConverters.add(jsonMessageConverter);
restTemplate.setMessageConverters(messageConverters);
MyResponse response =
    restTemplate.exchange(request, new ParameterizedTypeReference<MyResponse>() {
    }).getBody();

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.