java - (Jackson) 如何使用另一个对象的属性将 JSON 反序列化为具有多态性的 POJO?

标签 java json spring serialization jackson

假设我有一个包含 2 个对象的 json,“header”和“data”。它们都有等效的模型,但只有 Data 具有可变属性,而 Header 属性始终相同。 “数据”上的属性是可变的,应根据“ header ”中的字段进行反序列化,例如:

// this should be deserialized to SuperPojo
{
  "header": {
    "type": "y" // this should be used as a rule for deserializing object "data"
  },
  "data": { // this should be deserialized into any Data subclass
    // variable data
  }
}

这些是我想将上述 JSON 反序列化为的 bean:

// wrapper object
@JsonIgnoreProperties(ignoreUnknown = true)
public class SuperPojo<T extends Data> {
    private Header header;
    private T data;
    // getters & setters
}

@JsonIgnoreProperties(ignoreUnknown = true)
public class Header implements Serializable {
    private String type;
    // getters & setters
}

// Marker class without fields. Used for inheritance and polymorphism purposes only
// the issue here is that Jackson tries to find a field named "type"
// within "Data" subclasses. What I want is for it to search the "type"
// attribute in class "Header".
@JsonTypeInfo(
    use = JsonTypeInfo.Id.CLASS,
    include = JsonTypeInfo.As.EXTERNAL_PROPERTY,
    property = "type")
@JsonSubTypes({
    @JsonSubTypes.Type(value = DataX.class, name="x"), // how do I make this refer to field "type" in object "Header"?
    @JsonSubTypes.Type(value = DataY.class, name="y") 
})
public class Data implements Serializable { }

// should be deserialized into my SuperPojo when header.type equals "x"
@JsonTypeName("x")
public class DataX extends Data {
    private long id;
    private String name;
    // getters & setters
}

// should be deserialized into my SuperPojo when header.type equals "y"
@JsonTypeName("y")
public class DataY extends Data {
    @JsonProperty("registration_date")
    private LocalDateTime registrationDate;
    private Country country; // enum
    // getters & setters
}

最后,这就是我尝试执行反序列化的方式:

// JSON X
{
  "header": {
    "type": "x"
  },
  "data": {
    "id": "12345",
    "name":"Jackson"
  }
}

// JSON Y
{
  "header": {
    "type": "y"
  },
  "data": {
    "registration_date": "2018-05-01T18:10:00",
    "country":"BRAZIL"
  }
}

@Service
public class MyTest {

    @Autowired
    private ObjectMapper mapper;

    void doStuff() {
        String jsonX = getJsonX(); // JSON X
        String jsonY = getJsonY(); // JSON Y
        SuperPojo<? extends Data> bigPojoX = mapper.readValue(jsonX, new TypeReference<SuperPojo<? extends Data>>() { });
        SuperPojo<? extends Data> bigPojoY = mapper.readValue(jsonY, new TypeReference<SuperPojo<? extends Data>>() { });
        DataX subPojoX = (DataX) bigPojoX.getData(); // throws ClassCastException
        DataY subPojoY = (DataY) bigPojoY.getData(); // throws ClassCastException
    }
}

我现在正在做的是,因为我想不出一种方法来完成这项工作:我正在将我的 json 反序列化为一个包装类,其中包含一个类型为“Header”的字段,然后获取并检查从它的“类型”并再次将 json 反序列化为上述包装类的子类,具有正确数据实现的属性。

我敢肯定这不是最好的方法,也不是一个好的设计选择,但这是我解决这个问题的唯一方法。

我已经搜索了一些可能的解决方案,但问题与我的有点不同,所以它们也没有奏效:

最佳答案

如果您只有简单的标量 id 作为 header,作为数据的同级,您可以按照指示使用 include = JsonTypeInfo.As.EXTERNAL_PROPERTY 轻松做到这一点。但它不适用于复杂的对象。 最好的选择是为包含数据和类型指示符的类型完全自定义反序列化器。

关于java - (Jackson) 如何使用另一个对象的属性将 JSON 反序列化为具有多态性的 POJO?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50124311/

相关文章:

java - 使用 Java split 计算数学表达式

java - JAXB 自定义和列表<Object>

java - Google oauth java 客户端获取访问 token 失败,出现 "400 Bad Request { "错误” : "invalid_request" }"

javascript - tid key 是否用 JSON 保留

json - Swift JSON 持续时间

java - 命名空间 '/' 没有在 JUNIT 中为范围 'request' 注册范围

java - 在线程对象上同步

android - picasso 图书馆和 GridView 图像

java - 如何在Spring中使用CommandLineRunner测试JPA实体?

java - 使用 Spring 4 PropertySource 时找不到可重复