解决FastJson中“$ref 循环引用检测”的问题的几种方式
一、现象:
项目中用json形式来存储一个集合对象,用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"
},
]
| 引用 | 描述 |
|---|---|
| "$ref":".." | 上一级 |
| "$ref":"@" | 当前对象,也就是自引用 |
| "$ref":"$" | 根对象 |
| "$ref":"$.children.0" | 基于路径的引用,相当于 root.getChildren().get(0) |
网上能找到的解决方案有:
将该对象在后端转换为json字符串返回给前端,使用SerializerFeature.DisableCircularReferenceDetect关闭循环引用。
String jsonString=JSON.toJSONString(object, SerializerFeature.DisableCircularReferenceDetect);
正常来说,我们的接口返回给前端的是List<Object>这种格式,如果采用这个方式,就要将返回值改为String,不优雅。当然也可以把这个字符串再次转换为对象,这样循环引用的问题就没有了。
可以在SpringBoot项目的json配置中将循环引用关闭。FastJson的.java配置增加以下项:
static {
// 全局配置关闭Fastjson循环引用,避免出现$ref
JSON.DEFAULT_GENERATE_FEATURE |= SerializerFeature.DisableCircularReferenceDetect.getMask();
}
/**
* FastJson配置类
*
* @author wyb
* @date 2023/01/01 00:00 周六
**/
@AutoConfiguration
public class FastJsonConfig {
static {
// 全局配置关闭Fastjson重复引用,避免出现$ref
JSON.DEFAULT_GENERATE_FEATURE |= SerializerFeature.DisableCircularReferenceDetect.getMask();
}
@Bean
public HttpMessageConverters fastJsonHttpMessageConverters() {
FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
List<MediaType> mediaTypes = new ArrayList<>();
mediaTypes.add(MediaType.APPLICATION_JSON);
/****************************FastJsonConfig Start*****************************/
com.alibaba.fastjson.support.config.FastJsonConfig fastJsonConfig = new com.alibaba.fastjson.support.config.FastJsonConfig();
// 序列化配置
SerializeConfig serializeConfig = new SerializeConfig();
// BigInteger、Long转JSON精度丢失配置
serializeConfig.put(BigInteger.class, ToStringSerializer.instance);
serializeConfig.put(Long.class, ToStringSerializer.instance);
serializeConfig.put(Long.TYPE, ToStringSerializer.instance);
// 序列化值为null的字段
fastJsonConfig.setSerializerFeatures(
SerializerFeature.PrettyFormat
, SerializerFeature.WriteNullListAsEmpty
, SerializerFeature.WriteMapNullValue
//, SerializerFeature.WriteNullStringAsEmpty
//, SerializerFeature.WriteNullNumberAsZero
//, SerializerFeature.WriteNullBooleanAsFalse
);
fastJsonConfig.setSerializeConfig(serializeConfig);
fastJsonConfig.setDateFormat("yyyy-MM-dd HH:mm:ss");
fastJsonConfig.setCharset(StandardCharsets.UTF_8);
/****************************FastJsonConfig End*****************************/
converter.setSupportedMediaTypes(mediaTypes);
converter.setFastJsonConfig(fastJsonConfig);
return new HttpMessageConverters(converter);
}
}
但是因为全局配置是在我们项目的基础jar包中配置的,改动基础jar包会有风险,会对前面所有的依赖项目产生影响。所以也不采用这种方式。
大家想想为什么fastjson默认是开启这个功能的就知道原因了。如果全局关闭,性能也会有极大影响。
我们可以将List中的对象使用BeanUtil这样的工具,拷贝为新的对象,然后放到新的集合中返回。
List<Object> orderListNew = new ArrayList<>();
orderList.forEach(i->{
Order target=new Order();
BeanUtils.copyProperties(i,target);
orderListNew.add(target);
});
return orderListNew;
BeanUtil创建出来的对象跟原来的对象不是同一个对象。需要额外的代码,有点丑。
如果循环引用的数据,前端用不到,那可以在实体类对应的字段加注解禁止序列化,这样前端就不会接收到这个字段的引用数据了。
@JSONField(serialize = false)
private List<Order> orderList;
这样在转换的时候,orderList就不会被转换了。
但是很多时候,我们又需要这个数据,所以禁止序列化也是不行滴,接下来看另外一种注解解决方式。
@JSONField(serialzeFeatures = {SerializerFeature.DisableCircularReferenceDetect})
private List<Object> objectList;
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐
所有评论(0)