递归对象到 JSON

Posted

技术标签:

【中文标题】递归对象到 JSON【英文标题】:Recursive objects to JSON 【发布时间】:2014-11-08 22:49:29 【问题描述】:

假设我有以下代码:

public class A 
    B b;

    public A() 
        this.b = new B(this);
    


public class B 
    A a;

    B(A a) 
        this.a = a;
    


如您所见,这些对象(AB)相互引用,如果您尝试将对象转换为 JSON 代码,则会导致无限递归:A 有 B,它与 A 相同,它具有相同的 B,等等,等等。

但是,如果我尝试将这些对象转换为 JSON,例如使用 Gson,由于递归(顺便说一句,这是完全合乎逻辑的),***Error 会引发。

现在 JSON 中有没有一种方法可以声明这样的递归?如果没有,那么有没有办法在 JSON 中处理这种递归?

还是我必须手动检查递归,删除它,将对象转换为 JSON,然后在将 JSON 字符串重建为 Java 对象时重新应用递归?

【问题讨论】:

您希望它们在 JSON 中的外观如何? 第一个问题:为什么会存在这种相互依赖关系?通常它是糟糕设计的一个指标。 您不能在 JSON 中表示具有循环的网络。网络必须是没有交叉连接的纯树。 @nablex - 网络中有循环的原因有很多。一个普通的旧双向链表,例如。 请注意,在任何其他语言中,您(通常)会编写自己的 JSON 对象转换函数,这不会有问题。但是在 Java 中工作的人被 Jacksonesque 工具所吸引,并没有学习如何处理纯 JSON。 【参考方案1】:

Jackson 有一些方法可以处理循环或双向依赖关系,特别是两种。

如果您必须将您的实体暴露给外界,我建议在导致循环引用的属性上添加@JsonIgnore。这将告诉杰克逊不要序列化该属性。但是,这意味着您必须在反序列化时自己再次引用它们(并且在序列化时,您必须向 JSON 数据结构添加一些内容才能知道在反序列化时再次引用什么)。

另一种方式,我怀疑这更符合您的喜好,是使用 Jackson 提供的双向功能。您可以使用@JsonManagedReference@JsonBackReference@JsonManagedReference 是属性的“转发”部分,它将正常序列化。 @JsonBackReference 是引用的“后面”部分;它不会被序列化,但会在“转发”类型被反序列化时重构。

您可以查看示例here。

下面我将展示其中一个应该澄清一些事情的例子

public class NodeList 
    @JsonManagedReference
    public List<NodeForList> nodes; //this one gets serialized


public class NodeForList 
    public String name;

    @JsonBackReference 
    public NodeList parent; //this one wont get serialized, but will be reconstructed upon deserialization

    public NodeForList()  this(null); 
    public NodeForList(String n)  name = n; 

【讨论】:

【参考方案2】:

虽然 JSON 与 JavaScript 的文字对象表示法非常相似,但它们并不完全相同。在这种情况下,差异很重要。

javascript 在内存中有一个 reference 的概念:许多变量可以指向同一个对象。 事实上,对于某些数据类型,这是直接烘焙到类型本身:具有相同值的所有变量,如果该值是这些类型之一,则指向同一个点。 StringBoolean 以这种方式工作。从技术上讲,Nullundefined 也是如此,尽管这一点没有实际意义,因为无论如何这些类型中的每一种都只有一个可能的值。

即使没有将引用嵌入到类型中,变量仍然可以引用内存中的同一个对象。

相比之下,JSON 没有引用的概念。一切都是新的和独特的价值,没有两件事可以指向同一个地方。 JSON 解析器解决了这种差异,JavaScript 的 eval 也是如此(尽管你不应该将 eval 与 JSON 一起使用);当每个值被读入内存时,它会被转换为适当类型的变量,如果这是一种类型,其中相同值的所有变量都指向同一个点,那么它们就会这样做。

但这只有在引用被烘焙到类型中时才有效; JSON 没有指定变量的方法,因此 JSON 文件中的任何点 可以 引用不同的点,do 引用不同的点。 这意味着您无法在 JSON 文件中执行递归。

相反,正如您所说,您的 JSON 生成器需要检查递归并将其删除,并用某种注释替换它,即该值应该引用其他内容。如何对这些音符进行编码取决于您;有很多方法可以做到这一点,有些方法会比其他方法更好地满足您的应用程序的特定需求。无论如何,不​​管你怎么做,另一端的 JSON 解析器都需要查找这些注释并将它们转换回递归引用。

另一种方法是使用其他格式,该格式具有某种本地编码引用的方法。

【讨论】:

【参考方案3】:

如果您只想转换为 Json 然后再返回,您可以将 Jackson 与 @JsonIdentityInfo 注释一起使用。它不会尝试存储对象本身,而是存储引用。

对象身份处理见此处:http://wiki.fasterxml.com/JacksonFeatureObjectIdentity

【讨论】:

以上是关于递归对象到 JSON的主要内容,如果未能解决你的问题,请参考以下文章

从 SimpleXML 对象到数组的递归转换

python小功能-递归解析Json

如何用java递归生成带children的json串

如何用java递归生成带children的json串

接口返回json对象出现套娃递归问题 | System.Text.Json 版本

JSON对象中的递归数据