FastJSON 重复引用($ref: "$.list[2])

参考文章
https://www.jianshu.com/p/6041242405e8

现象

最近项目开发中,遇到 Service 接口返回数据正常,经过API层后返回给端上的数据出现了 $ref: $.list[1]
在这里插入图片描述
导致端上数据解析异常。

问题原因

出现这种问题的原因是项目 API 层在给端上数据的时候会使用FastJSON 将对象转为 JSON 字符串,在解析对象的时候为了避免因对象的重复引用出现栈溢出异常,会对对象进行循环引用检测,默认如果重用对象的话,会使用引用的方式进行引用对象。因 Service 接口返回的数据中有对象被重复引用,因此出现该问题。

何为重复/循环引用

简单说,重复引用就是一个集合/对象中的多个元素/属性同时引用同一对象,循环引用就是集合/对象中的多个元素/属性存在相互引用导致循环。

重复引用
    @Test
    public void testFastJSON(){
        List<Object> list = Lists.newArrayList();
        Object obj = new Object();
        list.add(obj);
        list.add(obj);
        String s = JSON.toJSONString(list);
        System.out.println(s);
    }

输出结果:
[{},{"$ref":"$[0]"}]

循环引用
        // 循环引用的特殊情况,自引用
        Map<String, Object> map = new HashMap<>();
        map.put("map", map);
        System.out.println("自引用 Map:" + JSON.toJSONString(map));
        // map1引用了map2,而map2又引用map1,导致循环引用
        Map<String, Object> map1 = new HashMap<>();
        Map<String, Object> map2 = new HashMap<>();
        map1.put("map", map2);
        map2.put("map", map1);
        System.out.println("循环引用Map1: " + JSON.toJSONString(map1));
        System.out.println("循环引用Map2: " + JSON.toJSONString(map2));

输出结果:
自引用 Map:{"map":{"$ref":"@"}}
循环引用Map1: {"map":{"map":{"$ref":".."}}}
循环引用Map2: {"map":{"map":{"$ref":".."}}}

一般来说,存在循环引用问题的集合/对象在序列化时(比如Json化),如果不加以处理,会触发StackOverflowError异常。

分析原因:当序列化引擎解析map1时,它发现这个对象持有一个map2的引用,转而去解析map2。解析map2时,发现他又持有map1的引用,又转回map1。如此产生StackOverflowError异常。

引用标识说明:

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

解决方法

临时解决方案(局部)

该 API 接口返回数据时进行特殊处理:

String s = JSON.toJSONString(re1, SerializerFeature.DisableCircularReferenceDetect);
re1 = JSON.parseObject(s,Result.class);

Spring 项目(全局)

<mvc:annotation-driven>  
        <mvc:message-converters register-defaults="true">  
            <bean  
                class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">  
                <property name="supportedMediaTypes">  
                    <array>  
                        <value>text/html;charset=UTF-8</value>  
                    </array>  
                </property>  
                <property name="features">  
                    <array>  
                        <value>WriteMapNullValue</value>  
                        <value>WriteNullStringAsEmpty</value>  
                        <!-- 全局关闭循环引用检查,最好是不要关闭,不然有可能会StackOverflowException -->
                        <value>DisableCircularReferenceDetect</value>
                    </array>  
                </property>  
            </bean>  
        </mvc:message-converters>  
    </mvc:annotation-driven>  
SpringBoot 项目
public class FastJsonHttpMessageConverterEx extends FastJsonHttpMessageConverter{
    public FastJsonHttpMessageConverterEx(){
        //在这里配置fastjson特性(全局设置的)
        FastJsonConfig fastJsonConfig = new FastJsonConfig();
        //fastJsonConfig.setDateFormat("yyyy-MM-dd HH:mm:ss");    //自定义时间格式
        //fastJsonConfig.setSerializerFeatures(SerializerFeature.WriteMapNullValue);  //正常转换null值
        //fastJsonConfig.setSerializerFeatures(SerializerFeature.DisableCircularReferenceDetect);     //关闭循环引用
        this.setFastJsonConfig(fastJsonConfig);
    }

    @Override
    protected boolean supports(Class<?> clazz) {
        return super.supports(clazz);
    }
}

@Configuration
public class WebMvcConfigurer extends WebMvcConfigurerAdapter {
    .....
    @Bean
    public FastJsonHttpMessageConverterEx fastJsonHttpMessageConverterEx(){
        return new FastJsonHttpMessageConverterEx();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值