Reputation: 168
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
Upvotes: 4
Views: 16781
Reputation: 7563
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();
Upvotes: 1
Reputation: 28519
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;
Upvotes: 8