基于唯一属性的存在,使用 Jackson 反序列化多态类型更新

2022-08-31 21:28:01

如果我有这样的类结构:

public abstract class Parent {
    private Long id;
    ...
}

public class SubClassA extends Parent {
    private String stringA;
    private Integer intA;
    ...
}

public class SubClassB extends Parent {
    private String stringB;
    private Integer intB;
    ...
}

有没有另一种方法可以反序列化不同呢?在我的父类上使用此注释:@JsonTypeInfo

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "objectType")

我宁愿不必强制我的API的客户端包含反序列化子类。"objectType": "SubClassA"Parent

Jackson 是否提供了一种注释子类并通过唯一属性将其与其他子类区分开来的方法,而不是使用 。在我上面的示例中,这将是这样的,“如果一个JSON对象将其反序列化为,如果它将其反序列化为”。@JsonTypeInfo"stringA": ...SubClassA"stringB": ...SubClassB


答案 1

这是我想出的一个解决方案,它扩展了Erik Gillespie的解决方案。它完全符合您的要求,并且对我有用。

使用 Jackson 2.9

@JsonDeserialize(using = CustomDeserializer.class)
public abstract class BaseClass {

    private String commonProp;
}

// Important to override the base class' usage of CustomDeserializer which produces an infinite loop
@JsonDeserialize(using = JsonDeserializer.None.class)
public class ClassA extends BaseClass {
    
    private String classAProp;
}

@JsonDeserialize(using = JsonDeserializer.None.class)
public class ClassB extends BaseClass {
    
    private String classBProp;
}

public class CustomDeserializer extends StdDeserializer<BaseClass> {

    protected CustomDeserializer() {
        super(BaseClass.class);
    }

    @Override
    public BaseClass deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
        TreeNode node = p.readValueAsTree();
        
        // Select the concrete class based on the existence of a property
        if (node.get("classAProp") != null) {
            return p.getCodec().treeToValue(node, ClassA.class);
        }
        return p.getCodec().treeToValue(node, ClassB.class);
    }
}

// Example usage
String json = ...
ObjectMapper mapper = ...
BaseClass instance = mapper.readValue(json, BaseClass.class);

如果你想变得更花哨,你可以扩展以包括一个映射属性名称的属性名称,当存在时,该属性名称将映射到特定的类。本文介绍了这种方法。CustomDeserializerMap<String, Class<?>>

更新

Jackson 2.12.0 从可用字段获取多态子类型演绎,这增加了 !@JsonTypeInfo(use = DEDUCTION)

AsDeductionTypeDeserializer 实现从字段中推断子类型的推断演绎。作为一个不打算合并的POC,tere需要大量的剪切粘贴代码等,但我认为功能性PR将是讨论我出于兴趣而写的东西的最佳基础。

它的工作原理是在注册时对每个子类型的整套可能字段进行指纹识别。在反序列化时,可用字段与这些指纹进行比较,直到只剩下一个候选项。它专门只查看直系子字段名称,因为现有机制涵盖了直系子级值,而更深层次的分析是一项更具强制性的ML任务,而不是Jackson职权范围的一部分。

顺便说一句,有一个(现已关闭的)Github问题在这里请求:https://github.com/FasterXML/jackson-databind/issues/1627


答案 2

这感觉像是某种东西,应该用于,但我已经挑选了文档,没有一个可以提供的属性似乎与你所描述的完全匹配。@JsonTypeInfo@JsonSubTypes

您可以编写一个自定义反序列化程序,该反序列化程序以非标准方式使用“ ”name“和”value“属性来完成所需的操作。反序列化程序将在基类上提供,反序列化程序将使用“name”值来检查属性是否存在,如果存在,则将 JSON 反序列化为“value”属性中提供的类。然后,您的类将如下所示:@JsonSubTypes@JsonSubTypes

@JsonDeserialize(using = PropertyPresentDeserializer.class)
@JsonSubTypes({
        @Type(name = "stringA", value = SubClassA.class),
        @Type(name = "stringB", value = SubClassB.class)
})
public abstract class Parent {
    private Long id;
    ...
}

public class SubClassA extends Parent {
    private String stringA;
    private Integer intA;
    ...
}

public class SubClassB extends Parent {
    private String stringB;
    private Integer intB;
    ...
}