@JsonIdentityInfo 无法反序列化同一文件中的对象横截面

@JsonIdentityInfo fails to deserialize object cross sections within the same file

我使用 Java ObjectMapper 解析 JSON 对象。一些对象应该是可重用的,因此我使用 @JsonIdentityInfo 来解析对它们的引用(我能够将它们定义为内联或作为引用)。一开始是有效的。

但是,我确实有2种类型的对象,它们可能相互引用(那里有继承,有些对象是复合对象)。

我的输入文件被分成几个部分。现在,我的问题是部分之间的交叉引用。如果在第 1 部分中读取了引用的对象,则可以。但是,如果引用的对象位于第二部分,则不会解决该问题。我不想强制执行命令,因为可能会有交叉引用。

有没有办法强制 ObjecMapper 在失败之前查看整个输入?

例如,这些是我的 classes(非常简化我的用例):

@Getter
@Setter
@JsonTypeInfo(
    use = JsonTypeInfo.Id.NAME,
    property = "@type",
    visible = true)
@JsonSubTypes({
    @JsonSubTypes.Type(value = AValue.class, name = "AValue"),
    @JsonSubTypes.Type(value = AReferenceToB.class, name = "AReferenceToB"),
})
@JsonIdentityInfo(scope = A.class, generator = ObjectIdGenerators.PropertyGenerator.class)
public abstract class A {
    @JsonProperty("@id")
    private String id;
    @JsonProperty("@type")
    private String type;
}

@Getter
@Setter
public class AValue extends A {
    @NonNull
    String value;

    @JsonCreator
    public AValue(@JsonProperty(value = "value", required = true) @NonNull String value) {
        this.value = value;
    }
}

@Getter
@Setter
public class AReferenceToB extends A{
    @NonNull
    B b;

    @JsonCreator
    public AReferenceToB(@JsonProperty(value = "b", required = true) @NonNull B b) {
        this.b = b;
    }
}

@Getter
@Setter
@JsonTypeInfo(
    use = JsonTypeInfo.Id.NAME,
    property = "@type",
    visible = true)
@JsonSubTypes({
    @JsonSubTypes.Type(value = BValue.class, name = "BValue"),
    @JsonSubTypes.Type(value = BReferenceToA.class, name = "BReferenceToA"),
})
@JsonIdentityInfo(scope = B.class, generator = ObjectIdGenerators.PropertyGenerator.class)
public class B {
    @JsonProperty("@id")
    private String id;
    @JsonProperty("@type")
    private String type;
}

@Getter
@Setter
public class BValue extends B {
    int value;

    @JsonCreator
    public BValue(@JsonProperty(value = "value", required = true) int value) {
        this.value = value;
    }
}

@Getter
@Setter
public class BReferenceToA extends B {
    @NonNull
    A a;

    @JsonCreator
    public BReferenceToA(@JsonProperty(value = "a", required = true) @NonNull A a) {
        this.a = a;
    }
}

这是我将 JSON 读入的 class:

@Getter
@Setter
public class File {
    @NonNull
    private List<A> as;

    @NonNull
    private List<B> bs;

    @JsonCreator
    public File(@JsonProperty(value = "as", required = true) @NonNull List<A> as,
                @JsonProperty(value = "bs", required = true) @NonNull List<B> bs) {
        this.as = as;
        this.bs = bs;
    }
}

在没有 a2 的情况下读取此文件成功,但会因 a2 而失败。如果我也交换 asbs,它将不起作用。

{
    "as": [
        {
            "@id": "a1",
            "@type": "AValue",
            "value": "test"
        },
        {
            "@id": "a2",
            "@type": "AReferenceToB",
            "b": "b1"
        }

    ],
    "bs": [
        {
            "@id": "b1",
            "@type": "BValue",
            "value": 1
        },
        {
            "@id": "b2",
            "@type": "BReferenceToA",
            "a": "a1"
        }

    ]
}

失败:

com.fasterxml.jackson.databind.exc.InvalidTypeIdException: Could not resolve subtype of [simple type, class A]: missing type id property '@type' (for POJO property 'as')
 at [Source: (File); line: 12, column: 9] (through reference chain: File["as"]->java.util.ArrayList[1])

代码:

ObjectMapper mapper = new ObjectMapper();
final File file = mapper.readValue(Paths.get("test.json").toFile(), File.class);

简介

让我们将以下 Maven 依赖项的子集视为 Jackson 库的当前版本:

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
    <version>2.13.2</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.13.2.2</version>
</dependency>

回答

目前 Jackson 库不支持 @JsonProperty@JsonIdentityInfo 和集合(List<E> 接口)的这种用法。

请查看未解决的 GitHub 问题:Correctly deserialize forward @JsonIdentityInfo references when using @JsonCreator · Issue #3030 · FasterXML/jackson-databind

请注意 the comment 关于 GitHub 问题:

cowtowncoder commented on Jan 30, 2021

Correct: there may be cases with combination of @JsonCreator, identity info, and ordered collections (Lists and arrays) that may not be possible support ever, at all. Calling constructor is not possible without having actual object, and conversely values of collections must be deserialized in order. You may need to change the usage so that List properties in question are passed by setters or fields; or possibly use Builder-style if immutability is required (builders work as long as built type itself does not use object id; its properties can use them).

解决方法

已在 GitHub 问题的评论中描述了一些解决方法。

解决方法示例:使用字段而不是构造函数进行反序列化

示例工作正常,在更新 AReferenceToB class 以使用字段而不是构造函数进行反序列化后:

@Getter
@Setter
public class AReferenceToB extends A {
    @JsonProperty(value = "b", required = true)
    @NonNull
    B b;
}

解决方法的缺点:

  • 它引入了可变性。 AReferenceToB class 已变得可变。