解决FastJson中“$ref 循环引用检测”的问题的几种方式

简介: 解决FastJson中“$ref 循环引用检测”的问题的几种方式

 

一、现象:

项目中用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"
  },
]

image.gif

二、问题分析:$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);

      image.gif

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

      2.全局配置关闭

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

      fastConverter.setFeatures(SerializerFeature.DisableCircularReferenceDetect);

      image.gif

      但是因为全局配置是在我们项目的基础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;

      image.gif

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

      4.禁止序列化

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

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

      image.gif

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

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

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

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

      image.gif

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

      参考资料:

        1. fastjson:对象转化成json出现$ref_琦彦的博客-CSDN博客
        相关文章
        |
        JSON fastjson Java
        fastjson:对象转化成json出现$ref
        fastjson:对象转化成json出现$ref
        |
        监控 druid Java
        Spring Boot 3 集成 Druid 连接池详解
        在现代的Java应用中,使用一个高效可靠的数据源是至关重要的。Druid连接池作为一款强大的数据库连接池,提供了丰富的监控和管理功能,成为很多Java项目的首选。本文将详细介绍如何在Spring Boot 3项目中配置数据源,集成Druid连接池,以实现更高效的数据库连接管理。
        13330 2
        Spring Boot 3 集成 Druid 连接池详解
        |
        存储 Java 调度
        开发踩坑记录之二:谨慎使用Spring中的@Scheduled注解
        在一些业务场景中需要执行定时操作来完成一些周期性的任务,比如每隔一周删除一周前的某些历史数据以及定时进行某项检测任务等等。在日常开发中比较简单的实现方式就是使用Spring的@Scheduled(具体使用方法不再赘述)注解。但是在修改服务器时间时会导致定时任务不执行情况的发生,解决的办法是当修改服务器时间后,将服务进行重启就可以避免此现象的发生。本文将主要探讨服务器时间修改导致@Scheduled注解失效的原因,同时找到在修改服务器时间后不重启服务的情况下,定时任务仍然正常执行的方法。 @Scheduled失效原因分析 解析流程图 使用新的方法
        开发踩坑记录之二:谨慎使用Spring中的@Scheduled注解
        |
        数据可视化 Java Nacos
        OpenFeign + Sentinel 实现微服务熔断限流实战
        本文介绍如何在Spring Cloud微服务架构中,结合OpenFeign与阿里巴巴开源组件Sentinel,实现服务调用的熔断、降级与限流。通过实战步骤搭建user-service与order-service,集成Nacos注册中心与Sentinel Dashboard,演示服务异常熔断、QPS限流控制,并支持自定义限流响应。借助Fallback降级机制与可视化规则配置,提升系统稳定性与高可用性,助力构建健壮的分布式应用。
        1130 155
        |
        9月前
        |
        存储 搜索推荐 数据库
        🚀 RAGFlow Docker 部署全流程教程
        RAGFlow是开源的下一代RAG系统,融合向量数据库与大模型,支持全文检索、插件化引擎切换,适用于企业知识库、智能客服等场景。支持Docker一键部署,提供轻量与完整版本,助力高效搭建私有化AI问答平台。
        7981 8
        |
        前端开发 fastjson Java
        我的字段被FastJson给干掉了?!
        本文记录作者升级到 JDK 11 后遇到的 FastJSON 序列化问题,以及详细的排查过程。
        623 12
        |
        算法 物联网 数据库
        阿里云 OpenSearch RAG 应用实践
        本文介绍了阿里云OpenSearch在过去一年中在RAG方面的应用和探索。
        29094 4
        阿里云 OpenSearch RAG 应用实践
        |
        存储 JSON fastjson
        再也不用心惊胆战地使用FastJSON了——序列化篇
        本篇将主要介绍json序列化的详细流程。本文阅读的FastJSON源码版本为2.0.31。
        4620 49
        |
        消息中间件 供应链 测试技术
        图解 DDD,这一篇总结太全面了!
        DDD领取驱动是非常热的架构设计,微服务也有大量涉及,本文详细解析领域驱动设计(DDD),涵盖DDD原理、实践步骤及核心概念等,帮助更好地管理复杂业务逻辑。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
        图解 DDD,这一篇总结太全面了!

        热门文章

        最新文章