β

解决fastjson内存对象引用/循环引用导致json中出现$ref

赵伊凡's Blog 55 阅读

目前公司有一部分数据使用的是类似于 redis 的一种自研 数据库 进行存储的。对于一般的对象存储,我们会把对象转换为json字符串之后,再进行序列化存储。

前段时间上线,出现一个线上问题,发现json反解析出错。立刻摘除节点,追踪具体json问题。发现json内容中包含$ref部分,可以确定是这里的问题。

通过搜索,知道原来fastjson为了防止对象重复和循环引用,做了这么一个优化,即如果出现重复或者循环引用,则使用$ref来表示重复或循环的部分。

什么是重复引用呢?

比如一个对象,放到数组里面,而这个对象是在外面new的,然后循环的时候去set里面的其中一个值,然后放到list里面去。这样做其实list里面放的一直都是同一个对象。这样就产生了对象的重复引用。

什么是循环引用呢?

A对象里面有B类型的属性,B里面有A类型的属性。这样如果对A进行json序列化,那么会产生StackOverflowError。因为A里面引用了B,B里面引用了A,无限循环下去,没完没了。

fastjson如何解决这两个问题

fastjson对对象进行json序列化,不可避免的就会遇到循环引用的问题,为了避免这个问题,fastjson在对对象进行序列化的时候,如果遇到了重复引用、循环引用的时候,默认会以“引用标识”来处理这部分内容,从而防止StackOverflowError的产生。

简单介绍下引用标识

“$ref”:”..” 上一级
“$ref”:”@” 当前对象,也就是自引用
“$ref”:”$” 根对象
“$ref”:”$.children.0” 基于路径的引用,相当于root.getChildren().get(0)
“$ref”:”$[0]" 数组、列表的第一个元素

我们出现的情况就是在循环列表的时候循环改对象的内容并加入list,最后对list进行序列化,产生的“$ref”:”$[0]"的情况。由于只是重复引用的问题,确认并不会产生StackOverflowError情况,所以在序列化的时候增加参数SerializerFeature.DisableCircularReferenceDetect取消循环引用优化,达到目的。

代码如下:

JSON.toJSONString(object, SerializerFeature.DisableCircularReferenceDetect);

当然人家默认就是有对循环引用做了优化的,所以 不建议增加这个参数来处理这个问题 。如果出现了循环引用的话,就会产生StackOverflowError了。

如果避免重复引用、循环引用

在使用对象的时候(比如列表填充的时候),使用新对象而不是重复使用原有对象进行赋值。
不要两个对象互相引用对方。循环引用其实在对象设计的时候本来就是不合理的,这块不论是转json还是其他时候使用都是应该注意的。

本文原创于 赵伊凡BLOG 转载 请注明出处。

©原创文章,转载请注明来源: 赵伊凡's Blog
©本文链接地址: 解决fastjson内存对象引用/循环引用导致json中出现$ref

作者:赵伊凡's Blog
一个软件开发爱好者 专注于互联网应用开发 Java目前是我的饭碗

发表评论