使用上下文堆栈而不是使用 JsonIdentityInfo 或 JsonBackReference 和 JsonManagedReference 处理 Jackson 循环依赖

Handling Jackson circular dependencies using a context stack instead of using JsonIdentityInfo, or JsonBackReference & JsonManagedReference

我正在使用 Jackson 来序列化 JSON,但我的 JSON 客户端不是 Java,并且不要使用 Jackson。

我想避免循环引用序列化无限递归,每当序列化整个 bean 会导致无限递归时,序列化 bean 的 id 而不是整个 bean。

这可以通过维护当前序列化上下文的堆栈来实现。

每当 Jackson 开始序列化一个 bean 时,它应该查看堆栈是否包含该 bean。

如果bean已经在栈中,序列化id而不是整个bean,从而避免无限递归。

如果bean不在栈中,则将其压入栈中,并正常序列化整个bean。

Jackson 完成 bean 的序列化后,将 bean 弹出堆栈。

因此,我想将下面的Java序列化为下面的JSON:

Java:

class A {
    String id;
    B      b;
    B      c;
}

class B {
    String id;
    A      a;
}

class Main {
    public static void main(String[] args) {
        A a = new A();
        B b = new B();
        a.id = "a1";
        b.id = "b1";
        a.b = b;
        a.c = b;
        b.a = a;
        ObjectMapper m = new ObjectMapper();
        System.out.println(m.writeValueAsString(a));
        System.out.println(m.writeValueAsString(b));
    }
}

JSON:

{
    "id": "a1",
    "b": {
        "id": "b1",
        "a":  "a1"
    },
    "c": {
        "id": "b1",
        "a":  "a1"
    }
}

{
    "id": "b1",
    "a": {
        "id": "a1",
        "b":  "b1"
    }
}

我只知道 Jackson 内置的两种处理循环引用的方法,如果我理解正确的话,这两种方法都不能输出上面的 JSON:

  1. @JsonIdentityInfo,在最佳设置时,用以下方式注释 AB

    @JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
    

    会输出(带有说明问题的注释):

    {
        "id": "a1",
        "b": {
            "id": "b1",
            "a":  "a1"
        },
        "c": "b1" // problem: value of "c" is b.id instead of b with b.a = a.id
    }
    
    {
        "id": "b1",
        "a": {
            "id": "a1",
            "b":  "b1"
        }
    }
    
  2. @JsonManagedReference & @JsonBackReference,为此我必须指定一侧为前向,另一侧为后向。假设我选择 A -> B 属性作为转发,然后我得到:

    {
        "id": "a1",
        "b": {
            "id": "b1",
            "a":  "a1"
        },
        "c": {
            "id": "b1",
            "a":  "a1"
        }
    }
    
    {
        "id": "b1"
        // problem: "a" isn't serialized
    }
    

是否有任何内置的 Jackson 方式可以按照我的意愿进行序列化?

如果没有,Jackson 是否维护序列化堆栈?

如果是,我如何访问它并以某种方式修改序列化输出?

如果不是,我如何连接到 Jackson 以在我自己的 类 中维护一个实现一些 Jackson 接口/扩展一些 Jackson 类 的堆栈,然后使用该堆栈影响序列化?

Jackson 确实保持了几个堆栈。较旧的是 JsonGenerator 级别,参见 getOuputContext()。这让您可以看到物理上正在输出什么,即哪些范围(对象、数组)是打开的。

但是 Jackson 2.5 添加了新方法 JsonGenerator.getCurrentValue() 和来自数据绑定的匹配调用,这应该另外为您提供正在序列化的对象图视图,至少在将两者结合时是这样。 要访问当前值的祖先,您需要遍历 "output context",向输出堆栈询问当前值。