这里就是循环引用造成的。
一、问题分析:$ref:
- 接口返回的
api
通过fastjson
将实体转化为json
字符串时,在传输的数据中如果出现相同的对象,fastjson
默认开启引用检测会将相同的对象写成引用的形式; - 引用是通过
$ref
来表示的 引用分两种; - 循环引用和重复引用。
占位符 | 类型 |
---|---|
“$ref”:”..” | 上一级 |
“$ref”:”@” | 当前对象,也就是自引用 |
“$ref”:”$” | 根对象 |
“$ref”:”$.children.0″ | 基于路径的引用,相当于 root.getChildren().get(0) |
二、循环引用和重复引用
循环引用:即 A 对象引用 B 对象,B 对象又引用 A 对象,这种情况是要极力避免的,因为会导致堆栈溢出 (StackOverflowError);
三、$ref 问题的解决方案:
1. 局部关闭
将该对象在后端转换为 json 字符串返回给前端,使用 SerializerFeature.DisableCircularReferenceDetect
关闭循环引用。
String str=JSON.toJSONString(object,SerializerFeature.DisableCircularReferenceDetect);
正常来说,我们的接口返回给前端的是 List 这种格式,如果采用这个方式,就要将返回值改为 String,不 优雅。当然也可以把这个字符串再次转换为对象,这样循环引用的问题就没有了。
2. spring boot 项目
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.DisableCircularReference Detect); //关闭循环引用 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(); } }
3. 禁止序列化
如果循环引用的数据,前端用不到,那可以在实体类对应的字段加注解禁止序列化,这样前端就不会接 收到这个字段的引用数据了。
@JSONField(serialize = false) private List<Dept> dept
4. 普通 spring 项目
全局关闭循环引用检查:
<!-- 全局关闭循环引用检查,最好是不要关闭,不然有可能会 StackOverflowException --> <value>DisableCircularReferenceDetect</value>
在该字段的注解上指定序列化时关闭循环引用
//一个员工对应一个部门表 @JSONField(serialzeFeatures = SerializerFeature.DisableCircularReferenceDetect) private Dept dept=new Dept()
此种方法才是我们项目中的解决办法。