目录

一、现象:

二、问题分析:$ref

三、循环引用和重复引用

四、$ref问题的解决

1.局部关闭

2.全局配置关闭

3.new新的对象来避免

4.禁止序列化

5.在该字段的注解上指定序列化时关闭循环引用


一、现象:


项目中用json形式来存储一个集合对象,用fastjson发现多了一些东西:$ref,了解之后才发现是重复引用的问题。

[
    {
        "id":"1",
        "orderList":[
            {
                "id":2,
                "date":"2020-08-17 12:57:21",
                "name":"帽子"
            },
            {
                "id":3,
                "date":"2020-08-17 12:57:21",
                "name":"鞋子"
            }
        ],
        "remarks":""
    },
    {
        
        "id":"2",
        "orderList":[
            {"$ref":"$[0].orderList[0]"},
            {"$ref":"$[0].orderList[1]"}
        ],
        "remarks":"111"
    },
]

二、问题分析:$ref


接口返回的api通过fastjson将实体转化为json字符串时,在传输的数据中如果出现相同的对象,fastjson默认开启引用检测会将相同的对象写成引用的形式
引用是通过"$ref"来表示的
引用分两种,循环引用和重复引用
引用    描述

"$ref":".."    上一级
"$ref":"@"    当前对象,也就是自引用
"$ref":"$"    根对象
"$ref":"$.children.0"    基于路径的引用,相当于 root.getChildren().get(0)

三、循环引用和重复引用


1.循环引用:即A对象引用B对象,B对象又引用A对象,这种情况是要极力避免的,因为会导致堆栈溢出(StackOverflowError);
2.重复引用:上面的例子就是因为相同的订单对象出现在两个集合中,所以第二个orderList集合中直接返回的是$ref。一般大家在写代码的过程中,如果出现$ref,通常应该是重复引用问题。


四、$ref问题的解决


网上能找到的解决方案有:

1.局部关闭


将该对象在后端转换为json字符串返回给前端,使用SerializerFeature.DisableCircularReferenceDetect关闭循环引用。

String jsonString=JSON.toJSONString(object, SerializerFeature.DisableCircularReferenceDetect);

正常来说,我们的接口返回给前端的是List<Object>这种格式,如果采用这个方式,就要将返回值改为String,不优雅。当然也可以把这个字符串再次转换为对象,这样循环引用的问题就没有了。

2.全局配置关闭

可以在SpringBoot项目的json配置中将循环引用关闭。FastJson的.java配置增加以下项:

fastConverter.setFeatures(SerializerFeature.DisableCircularReferenceDetect);

但是因为全局配置是在我们项目的基础jar包中配置的,改动基础jar包会有风险,会对前面所有的依赖项目产生影响。所以也不采用这种方式。

大家想想为什么fastjson默认是开启这个功能的就知道原因了。如果全局关闭,性能也会有极大影响。

3.new新的对象来避免

我们可以将List中的对象使用BeanUtil这样的工具,拷贝为新的对象,然后放到新的集合中返回。

   List<Object> orderListNew = new ArrayList<>();
   orderList.forEach(i->{
       Order target=new Order();
       BeanUtils.copyProperties(i,target);
       orderListNew.add(target);
   });
   return orderListNew;

BeanUtil创建出来的对象跟原来的对象不是同一个对象。需要额外的代码,有点丑。

4.禁止序列化

如果循环引用的数据,前端用不到,那可以在实体类对应的字段加注解禁止序列化,这样前端就不会接收到这个字段的引用数据了。

    @JSONField(serialize = false)
    private List<Order> orderList;

这样在转换的时候,orderList就不会被转换了。

5.在该字段的注解上指定序列化时关闭循环引用

但是很多时候,我们又需要这个数据,所以禁止序列化也是不行滴,接下来看另外一种注解解决方式。

    @JSONField(serialzeFeatures = {SerializerFeature.DisableCircularReferenceDetect})
    private List<Object> objectList;

此种方法才是我项目中的解决办法。

GitHub 加速计划 / js / json
41.72 K
6.61 K
下载
适用于现代 C++ 的 JSON。
最近提交(Master分支:1 个月前 )
960b763e 4 个月前
8c391e04 6 个月前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐