杰克逊多态反序列化和序列化

3

我需要分别进行两种不同类型的调用。根据业务需求,我想要进行反序列化或序列化。

其中一种json类型:

{
  "type": "foo",
  "data": [{
    "someCommonProperty": "common property",
    "fooProperty": "foo specific property"
  },{
    "someCommonProperty": "common property1",
    "fooProperty": "foo specific property1"
  }]
}

另一种类型的 JSON:

{
  "type": "bar",
  "data": [{
    "someCommonProperty": "common property",
    "barProperty": "bar specific property",
    "type": "type1"
  },{
    "someCommonProperty": "common property1",
    "barProperty": "bar specific property1",
    "type": "type1"
  }]
}

我有以下的课程:
 public class Parent {

    private String type;

    @JsonTypeInfo(use = Id.NAME, property = "type", include = As.EXTERNAL_PROPERTY)
    @JsonSubTypes(value = { 
        @JsonSubTypes.Type(value = Foo.class, name = "foo"),
        @JsonSubTypes.Type(value = Bar.class, name = "bar") 
    })
    private List<AbstractData> data;

    // Getters and setters
 }

 public abstract class AbstractData {

    private String someCommonProperty;

    // Getters and setters
 }

 public class Foo extends AbstractData {

    private String fooProperty;

    // Getters and setters
 }

 public class Bar extends AbstractData {

    private String barProperty;
    private String type;
    // Getters and setters
 }

当我尝试以下反序列化操作时,写出Java对象作为JSON后,得到了空字符串。
ObjectMapper mapper = new ObjectMapper();
Parent parent = mapper.readValue(json, Parent.class);

我遇到了这个错误:

 com.fasterxml.jackson.databind.JsonMappingException: Unexpected token (START_OBJECT), expected START_ARRAY: need JSON Array to contain As.WRAPPER_ARRAY type information for class com.DemoJsonToJavaDeserialize.AbstractData
 at [Source: C:<projectpath>\target\classes\foo.json; line: 3, column: 12] (through reference chain: com.DemoJsonToJavaDeserialize.Parent["data"]->java.util.ArrayList[0])
1个回答

7

选项1

如果您可以访问 AbstractData 类,则可以考虑使用新的 2.12 版本的 Jackson Deduction(演绎)特性:

@JsonSubTypes(value = {
        @JsonSubTypes.Type(value = Foo.class),
        @JsonSubTypes.Type(value = Bar.class)
})
@JsonTypeInfo(use = JsonTypeInfo.Id.DEDUCTION)
public abstract class AbstractData {
    private String someCommonProperty;
   
   // getters setters
}

如果您的子类具有不同的属性名称,则无需注释FooBarParent:所有内容都将自动实例化。此时type json属性也变得无用。

(如果使用SpringBoot,可以将jackson-bom.version属性设置为升级spring boot中的jackson)

选项2

JsonTypeInfo.As.EXTERNAL_PROPERTYjavadoc说:请注意,此机制不能用于容器值(数组、集合...)。 我认为这是您的问题:您正在将其应用于List

如果您不能修改任何Foo / Barr / AbstractData类,则经典方法是使用自定义 Deserializer 。 在 Parent 类上:

    @JsonDeserialize(contentUsing = FooBarDeserializer.class)
    private List<AbstractData> data;

以及反序列化器:

public class FooBarDeserializer extends JsonDeserializer<AbstractData> {
    @Override
    public AbstractData deserialize(JsonParser jsonParser, DeserializationContext ctxt) throws IOException {
        JsonNode node = jsonParser.readValueAsTree();
        if(node.has("fooProperty"))
            return jsonParser.getCodec().treeToValue(node,Foo.class);
        else
            return jsonParser.getCodec().treeToValue(node,Bar.class);
    }
}

如果你找到一种利用你的Parent.type的解决方案,请分享,我很感兴趣。


谢谢。我已经接受了你的答案。我最终编写了自定义反序列化器,但是你的实现比我的更优雅。 - user11160452
第一个选项对我有用。谢谢 - Soroush Shemshadi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接