Jackson 多态反序列化和序列化

use*_*452 3 polymorphism json deserialization

我将两种不同类型的单独调用。我想根据业务需求进行反序列化或序列化。

一种 json 类型:

{
  "type": "foo",
  "data": [{
    "someCommonProperty": "common property",
    "fooProperty": "foo specific property"
  },{
    "someCommonProperty": "common property1",
    "fooProperty": "foo specific property1"
  }]
}
Run Code Online (Sandbox Code Playgroud)

另一种类型的json:

{
  "type": "bar",
  "data": [{
    "someCommonProperty": "common property",
    "barProperty": "bar specific property",
    "type": "type1"
  },{
    "someCommonProperty": "common property1",
    "barProperty": "bar specific property1",
    "type": "type1"
  }]
}
Run Code Online (Sandbox Code Playgroud)

我有如下课程

 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
 }
Run Code Online (Sandbox Code Playgroud)

当我尝试如下反序列化时,我在将 java 对象写为 json 时得到空字符串。

ObjectMapper mapper = new ObjectMapper();
Parent parent = mapper.readValue(json, Parent.class);
Run Code Online (Sandbox Code Playgroud)

我收到此错误:

 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])
Run Code Online (Sandbox Code Playgroud)

小智 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
}
Run Code Online (Sandbox Code Playgroud)

然后,无需注释Foo,BarParent:只要您的子类具有不同的属性名称,所有内容都会自动实例化。typejson 属性也变得毫无用处。

(如果使用SpringBoot,可以jackson-bom.version在spring boot内部设置属性升级jackson)

选项2

javadoc说:。我相信这是您的问题:您将其应用于.JsonTypeInfo.As.EXTERNAL_PROPERTYNote also that this mechanism can not be used for container values (arrays, Collections...List

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

    @JsonDeserialize(contentUsing = FooBarDeserializer.class)
    private List<AbstractData> data;
Run Code Online (Sandbox Code Playgroud)

和解串器:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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