【问题标题】:Using @JsonTypeInfo and @JsonSubTypes, Deserialize an Object of Concrete Subclass Without Base Class and Type Information使用@JsonTypeInfo 和@JsonSubTypes,反序列化一个没有基类和类型信息的具体子类的对象
【发布时间】:2021-06-25 03:57:57
【问题描述】:

使用@JsonTypeInfo@JsonSubTypes,是否可以在没有类型信息的情况下反序列化具体子类的对象?

假设我有一个抽象 Animal 类:

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 子类:

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;
    }
}

我知道我可以像这样基于其父类实例化一个子类对象:

{
    "type": "Dog",
    "name": "Jack",
    "breed": "shepherd"
}
Animal deserializedDog = objectMapper.readValue(dogJson, Animal.class);

但是,有时我想直接实例化子类对象而不使用父类:

{
    "name": "Jack",
    "breed": "shepherd"
}
Dog deserializedDog = objectMapper.readValue(dogJson, Dog.class);

子类对象将被发送到端点:

@PostMapping("/endpoint")
    public ResponseEntity<Dog> sendDataToJms(
            @RequestBody Dog request, @RequestHeader Map<String, String> headers) throws Exception {
    //...
}

有可能吗?

【问题讨论】:

  • 你尝试的时候发生了什么?
  • 如果我忽略 type 字段,我会得到这个:JSON parse error: Missing type id when trying to resolve subtype of ...
  • 当你明确提供Dog.class时,你不应该得到那个。
  • 是的,你是对的。 JSON 对象在 @PostMapping 注释下作为 @RequestBody Dog dog 传递时被反序列化。但是,我确实提供了子类名称,它不会自动使用Dog.class吗?
  • 提供了子类名称在哪里/如何?您的问题没有包含足够的详细信息,无法回答。

标签: java spring-boot rest jackson polymorphism


【解决方案1】:

我找到了解决办法。

您需要启用MapperFeature.USE_BASE_TYPE_AS_DEFAULT_IMPL,以便使用没有类型信息的具体子类直接反序列化对象。

根据官方文档,

MapperFeature.USE_BASE_TYPE_AS_DEFAULT_IMPL 是一个功能, 指定多态值的声明基类型是否为 如果没有明确的默认类,则用作“默认”实现 通过@JsonTypeInfo.defaultImpl 注解指定。

接下来,您需要自定义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;
}

【讨论】:

    【解决方案2】:

    你只需要像这样创建ObjectMapper

    ObjectMapper objectMapper = new ObjectMapper()
                    .addHandler(new DeserializationProblemHandler() {
                        @Override
                        public JavaType handleMissingTypeId(DeserializationContext ctxt,
                                                            JavaType baseType,
                                                            TypeIdResolver idResolver,
                                                            String failureMsg)
                                throws IOException {
                            return baseType;
                        }
                    });
    

    【讨论】:

    • 当ObjectMapper没有看到type字段时,它会尝试DeserializationProblemHandler来处理丢失的类型id,所以你可以添加一个新的DeserializationProblemHandler来返回JavaType你想要的,我' m 使用jackson 2.9.4,它没有MapperFeature.USE_BASE_TYPE_AS_DEFAULT_IMPL
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-18
    • 1970-01-01
    相关资源
    最近更新 更多