使用@JsonTypeInfo 和@JsonSubTypes,反序列化一个没有Base Class 和类型信息的具体子类的对象
Using @JsonTypeInfo and @JsonSubTypes, Deserialize an Object of Concrete Subclass Without Base Class and Type Information
使用@JsonTypeInfo
和@JsonSubTypes
,是否可以在没有类型信息的情况下反序列化具体子class的对象?
假设我有一个摘要 Animal
class:
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
@JsonSubTypes({
@JsonSubTypes.Type(value = Dog.class, name = "Dog"),
@JsonSubTypes.Type(value = Cat.class, name = "Cat")
})
public abstract class Animal {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
和一个Dog
子class:
public class Dog extends Animal {
private String breed;
public Dog() {
}
public Dog(String name, String breed) {
setName(name);
setBreed(breed);
}
public String getBreed() {
return breed;
}
public void setBreed(String breed) {
this.breed = breed;
}
}
我知道我可以根据父对象 class 实例化子对象 class,如下所示:
{
"type": "Dog",
"name": "Jack",
"breed": "shepherd"
}
Animal deserializedDog = objectMapper.readValue(dogJson, Animal.class);
但是,有时我想直接实例化子class对象而不使用父对象class:
{
"name": "Jack",
"breed": "shepherd"
}
Dog deserializedDog = objectMapper.readValue(dogJson, Dog.class);
子class对象将被发送到端点:
@PostMapping("/endpoint")
public ResponseEntity<Dog> sendDataToJms(
@RequestBody Dog request, @RequestHeader Map<String, String> headers) throws Exception {
//...
}
可能吗?
您只需要像这样创建 ObjectMapper
:
ObjectMapper objectMapper = new ObjectMapper()
.addHandler(new DeserializationProblemHandler() {
@Override
public JavaType handleMissingTypeId(DeserializationContext ctxt,
JavaType baseType,
TypeIdResolver idResolver,
String failureMsg)
throws IOException {
return baseType;
}
});
我找到了解决办法。
您需要启用 MapperFeature.USE_BASE_TYPE_AS_DEFAULT_IMPL
才能使用没有类型信息的具体子类直接反序列化对象。
根据官方文档,
MapperFeature.USE_BASE_TYPE_AS_DEFAULT_IMPL
is a feature that
specifies whether the declared base type of a polymorphic value is to
be used as the "default" implementation, if no explicit default class
is specified via @JsonTypeInfo.defaultImpl
annotation.
接下来,您需要自定义 MappingJackson2HttpMessageConverter
,将 JSON 请求转换为 Java 对象,如下所示。
@Bean
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter();
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.enable(MapperFeature.USE_BASE_TYPE_AS_DEFAULT_IMPL);
jsonConverter.setObjectMapper(objectMapper);
return jsonConverter;
}
使用@JsonTypeInfo
和@JsonSubTypes
,是否可以在没有类型信息的情况下反序列化具体子class的对象?
假设我有一个摘要 Animal
class:
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
@JsonSubTypes({
@JsonSubTypes.Type(value = Dog.class, name = "Dog"),
@JsonSubTypes.Type(value = Cat.class, name = "Cat")
})
public abstract class Animal {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
和一个Dog
子class:
public class Dog extends Animal {
private String breed;
public Dog() {
}
public Dog(String name, String breed) {
setName(name);
setBreed(breed);
}
public String getBreed() {
return breed;
}
public void setBreed(String breed) {
this.breed = breed;
}
}
我知道我可以根据父对象 class 实例化子对象 class,如下所示:
{
"type": "Dog",
"name": "Jack",
"breed": "shepherd"
}
Animal deserializedDog = objectMapper.readValue(dogJson, Animal.class);
但是,有时我想直接实例化子class对象而不使用父对象class:
{
"name": "Jack",
"breed": "shepherd"
}
Dog deserializedDog = objectMapper.readValue(dogJson, Dog.class);
子class对象将被发送到端点:
@PostMapping("/endpoint")
public ResponseEntity<Dog> sendDataToJms(
@RequestBody Dog request, @RequestHeader Map<String, String> headers) throws Exception {
//...
}
可能吗?
您只需要像这样创建 ObjectMapper
:
ObjectMapper objectMapper = new ObjectMapper()
.addHandler(new DeserializationProblemHandler() {
@Override
public JavaType handleMissingTypeId(DeserializationContext ctxt,
JavaType baseType,
TypeIdResolver idResolver,
String failureMsg)
throws IOException {
return baseType;
}
});
我找到了解决办法。
您需要启用 MapperFeature.USE_BASE_TYPE_AS_DEFAULT_IMPL
才能使用没有类型信息的具体子类直接反序列化对象。
根据官方文档,
MapperFeature.USE_BASE_TYPE_AS_DEFAULT_IMPL
is a feature that specifies whether the declared base type of a polymorphic value is to be used as the "default" implementation, if no explicit default class is specified via@JsonTypeInfo.defaultImpl
annotation.
接下来,您需要自定义 MappingJackson2HttpMessageConverter
,将 JSON 请求转换为 Java 对象,如下所示。
@Bean
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter();
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.enable(MapperFeature.USE_BASE_TYPE_AS_DEFAULT_IMPL);
jsonConverter.setObjectMapper(objectMapper);
return jsonConverter;
}