使用@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;
}