递归对象转换为JSON

8
假设我有以下代码:
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,B又有同样的A,A又有同样的B,依此类推。
但是,如果我尝试将这些对象转换为JSON,例如使用Gson,那么由于递归而引发StackOverflowError(这完全合理)。
现在,在JSON中是否有一种声明此类递归的方式?如果没有,那么是否有一种处理这种递归的方法?
还是我必须手动检查递归,删除它,将对象转换为JSON,然后在重建JSON字符串以Java对象时重新应用递归?

1
你期望它们在JSON中是什么样子? - tom
第一个问题:为什么存在这种相互依赖?通常这是糟糕设计的指标。 - nablex
1
你不能用JSON表示带有循环的网络。该网络必须是一个纯树形结构,没有交叉连接。 - Hot Licks
1
@nablex - 网络中存在循环的原因有很多。一个简单的双向链表,例如。 - Hot Licks
请注意,在任何其他语言中,您通常会编写自己的JSON <==>对象转换函数,这不是问题。但是在Java中工作的人们被Jacksonesque工具所吸引,不学习如何处理纯JSON。 - Hot Licks
显示剩余3条评论
3个回答

3

Jackson有一些处理循环或双向依赖的方法,其中有两种特别常用。

如果您必须将实体暴露给外部世界,我建议在引起循环引用的属性上添加@JsonIgnore。这将告诉Jackson不要序列化该属性。然而,这意味着您必须在反序列化时自己再次引用它们(并且在序列化时,您必须向JSON数据结构添加某些内容以知道在反序列化时要引用什么)。

另一种方法,我猜这更符合您的喜好,是使用Jackson提供的双向特性。您可以使用@JsonManagedReference@JsonBackReference@JsonManagedReference是属性的“前向”部分,它将被正常序列化。 @JsonBackReference是参考的“后向”部分;它不会被序列化,但在“前向”类型反序列化时将被重建。
您可以在此处查看示例。
下面我将展示其中一个示例,应该能澄清一些问题。
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; }
}

1
尽管JSON非常类似于JavaScript的字面对象表示法,但它们并不完全相同。这是一个区别很重要的情况。
JavaScript在内存中有一个“引用”的概念:许多变量可以指向同一个对象。实际上,对于某些数据类型,这已经直接嵌入到类型本身中:如果这个值是这些类型之一,所有相同值的变量都指向同一个位置。例如,String和Boolean就是这样。从技术上讲,Null和Undefined也是如此,尽管每种类型只有一个可能的值,所以这一点无关紧要。
即使当引用没有嵌入到类型中时,变量仍然有可能引用内存中的同一个对象。 相比之下,JSON没有引用的概念。每个值都是新的和独立的,没有两个指向同一个地方的事物。JSON解析器弥补了这个差异,JavaScript的eval也是如此(尽管您不应该在JSON中使用eval);当每个值读入内存时,它会转换为适当类型的变量,如果这是一种类型,所有相同值的变量都指向同一位置,则它们将指向同一位置。

但这仅在引用嵌入类型时有效;JSON没有指定变量的方法,因此在JSON文件中可以指向不同位置的任何点都会指向不同位置。 这意味着您无法在JSON文件中执行递归操作。

相反,正如您所说,您的JSON生成器需要检查递归并删除它,并替换为某种提示,以指示该值应该引用其他内容。如何编码这些提示取决于您; 有许多方法可用,其中一些对您的应用程序的特定需求将比其他方法更好。无论如何,您的另一个JSON解析器需要搜索这些提示并将它们转换回递归引用。

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

0

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

有关对象标识处理,请参见此处:http://wiki.fasterxml.com/JacksonFeatureObjectIdentity


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接