首页 > 解决方案 > 识别、分组和处理对象总是由 id 单独序列化

问题描述

以下设置在 Java 11 上运行 jackson-databind (v 2.10.3)。

我有以下示例类来展示我的情况:Root、Trunk Leaf。它们有一些来回引用,语义可以忽略。

@JsonIdentityInfo(
    generator = ObjectIdGenerators.PropertyGenerator.class,
    scope = Root.class,
    property = "id")
public class Root {
  @JsonProperty
  private String id;
  @JsonProperty
  @JsonIdentityReference(alwaysAsId = true) // Marker1
  private List<Trunk> trunks;

  public Root() {
  }

  public Root(
      String id,
      List<Trunk> trunks) {
    this.id = id;
    this.trunks = trunks;
  }

  public String getId() {
    return id;
  }

  public void setId(String id) {
    this.id = id;
  }

  public List<Trunk> getTrunks() {
    return trunks;
  }

  public void setTrunks(List<Trunk> trunks) {
    this.trunks = trunks;
  }
}
@JsonIdentityInfo(
    generator = ObjectIdGenerators.PropertyGenerator.class,
    scope = Trunk.class,
    property = "id2")
//@JsonIdentityReference
public class Trunk {
  @JsonProperty
  private String id2;
  @JsonProperty
  @JsonIdentityReference(alwaysAsId = true)
  private Root backRoot;
  @JsonIdentityReference(alwaysAsId = true)
  @JsonProperty
  private Trunk next;
  @JsonProperty
  @JsonIdentityReference(alwaysAsId = true)
  private Leaf leaf;

  public Trunk() {
  }

  public Trunk(
      String id2) {
    this.id2 = id2;
  }

  public Root getBackRoot() {
    return backRoot;
  }

  public void setBackRoot(Root backRoot) {
    this.backRoot = backRoot;
  }

  public Trunk getNext() {
    return next;
  }

  public void setNext(Trunk next) {
    this.next = next;
  }

  public Leaf getLeaf() {
    return leaf;
  }

  public void setLeaf(Leaf leaf) {
    this.leaf = leaf;
  }
}
@JsonIdentityInfo(
    generator = ObjectIdGenerators.PropertyGenerator.class,
    scope = Leaf.class,
    property = "id")
public class Leaf {
  @JsonProperty
  private String id;
  @JsonProperty
  private String content;

  public Leaf() {}

  public Leaf(String id, String content) {
    this.id = id;
    this.content = content;
  }
}

目标是序列化(然后反序列化)对象图,注意有循环和引用多次出现。例如:

    var trunk1 = new Trunk("A");
    var trunk2 = new Trunk("N2");
    var leaf = new Leaf("LA", "38!");
    trunk1.setLeaf(leaf);
    trunk2.setLeaf(leaf);
    var test = new Root("A", List.of(trunk1, trunk2));
    trunk1.setBackRoot(test);
    trunk1.setNext(trunk2);
    trunk2.setBackRoot(test);
    trunk2.setNext(trunk2);

    String resultObjAsString = new ObjectMapper().writeValueAsString(test);

在当前设置中 resultObjAsString 等于(如预期){"id":"A","trunks":["A","N2"]}:。如果我删除 Marker1 处的行,则 resultObjAsString 等于

{
  "id": "A",
  "trunks": [
    {
      "id2": "A",
      "backRoot": "A",
      "next": "N2",
      "leaf": "LA"
    },
    {
      "id2": "N2",
      "backRoot": "A",
      "next": "N2",
      "leaf": "LA"
    }
  ]
}

正如预期的那样。正如 JsonIdentityReference 的 JavaDoc 中的注释告诉我们“反序列化可能需要额外的上下文信息”,即上述输出的反序列化当然不能恢复原始对象图。我不确定我的选择是什么。

我打算创建的是以下“标准化”输出:

{
  "root": {
    "A": {
      "id": "A",
      "trunks": [
        "A",
        "N2"
      ]
    }
  },
  "trunk": {
    "A": {
      "id2": "A",
      "backRoot": "A",
      "next": "N2",
      "leaf": "LA"
    },
    "N2": {
      "id2": "N2",
      "backRoot": "A",
      "next": "N2",
      "leaf": "LA"
    }
  },
  "leaf": {
    "LA": {
      "id": "LA",
      "content": "38!"
    }
  }
}

这意味着,虽然每个相关引用都与alwaysAsId=true上下文一起存储,但不会丢失,而是存储在输出的顶层。

我知道用jackson本地创建输出可能是不可能的,但我希望有一种方法可以访问所有作为引用存储的对象,以便以后枚举和单独存储它们的内容并编译总序列化输出。相反,我还需要一种向相关反序列化器提供反序列化对象的方法。

标签: javaserializationobjectmapperjackson-databind

解决方案


推荐阅读