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

新手上路,请多包涵

如果我有这样的类结构:

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

是否有另一种反序列化不同 then @JsonTypeInfo 的方法?在我的父类上使用此注释:

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

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

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

原文由 Sam Berry 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1k
2 个回答

这感觉像是 @JsonTypeInfo@JsonSubTypes 应该用于但我已经通过文档挑选并且可以提供的所有属性似乎都不符合您的描述。

您可以编写一个自定义反序列化器,以非标准方式使用 @JsonSubTypes ‘“名称”和“值”属性来完成您想要的。反序列化器和 @JsonSubTypes 将在您的基类上提供,反序列化器将使用“名称”值来检查属性是否存在,如果存在,则将 JSON 反序列化到“价值”属性。你的课程看起来像这样:

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

原文由 Erik Gillespie 发布,翻译遵循 CC BY-SA 3.0 许可协议

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

使用杰克逊 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);

如果您想变得更高级,可以扩展 CustomDeserializer 以包括一个 Map<String, Class<?>> 映射一个属性名称,如果存在,则映射到一个特定的类。 本文 介绍了这种方法。

更新

Jackson 2.12.0 从可用字段中获取多态子类型推导 @JsonTypeInfo(use = DEDUCTION)

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

它通过在注册时对每个子类型的全部可能字段进行指纹识别来工作。在反序列化时,将可用字段与那些指纹进行比较,直到只剩下一个候选者。它专门只查看直接子字段名称,因为现有机制涵盖了直接子值,而更深入的分析是一项更加艰巨的 ML 任务,并不是 Jackson 的职权范围。

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

原文由 bernie 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题