先说结论:
fastjson在把对象转换成字符串的时候,如果遇到相同的对象的时候,默认开启引用检测将相同的对象写成引用的形式。
官网文档:https://github.com/alibaba/fastjson/wiki
问题出现的背景:
在开发过程中,使用了第三方的拓扑图组件。组件生成图形的json字符串在利用 fastjson 转换成 JSON 对象的时候报错如下所示:
Exception in thread \"main\" com.alibaba.fastjson.JSONException: illegal ref, int
后来定位到如下的字符串解析的时候会出错:
public class test {public static void main(String[] args) {String str1 = \"{\\\"$position\\\":{\\\"$ref\\\":21}}\";String str2 = \"{\\\"position\\\":{\\\"ref\\\":21}}\";JSONObject json = JSONObject.parseObject(str1);}}
解析str2没问题,解析str1报错
Exception in thread \"main\" com.alibaba.fastjson.JSONException: illegal ref, int
刚开始判断是$符号是特殊符号导致,所以去掉后解析str2没问题。
后来又测试下去掉第一个$
String str1 = \"{\\\"position\\\":{\\\"$ref\\\":21}}\";
还是报错:Exception in thread \”main\” com.alibaba.fastjson.JSONException: illegal ref, int
那么问题出现在第二个
$ref
上了。那么怀疑$ref 就好像保留字段一样,不能使用,于是修改为
String str1 = \"{\\\"position\\\":{\\\"$reff\\\":21}}\";
那么调用
JSONObject.parseObject(str1);
果然就正常了。
查阅官网资料:fastjson支持循环引用,并且是缺省打开的。
如果有相同的对象,那么 fastjson 会默认开启引用模式。
语法 | 描述 |
---|---|
{\”$ref\”:\”$\”} | 引用根对象 |
{\”$ref\”:\”@\”} | 引用自己 |
{\”$ref\”:\”..\”} | 引用父对象 |
{\”$ref\”:\”../..\”} | 引用父对象的父对象 |
{\”$ref\”:\”$.members[0].reportTo\”} | 基于路径的引用 |
官网还给出了说明:
当序列化后的JSON传输到浏览器或者其他语言中,这些json解析器不支持循环引用,从而导致数据丢失。你可以关闭fastjson的循环引用支持。关闭引用检测,还能够提升序列化时的性能。
全局配置关闭
JSON.DEFAULT_GENERATE_FEATURE |= SerializerFeature.DisableCircularReferenceDetect.getMask();
非全局关闭
JSON.toJSONString(obj, SerializerFeature.DisableCircularReferenceDetect);
综上,我们的字符串中包括\”$ref\”,fastjson 认为这是引用模式,但是引用的值又是不正确的,导致出错。
解决方式上面说了两种,一种全局,一种非全局的。
我们采用非全局的,那么下面的代码就是正确✅的了
public class test {public static void main(String[] args) {String str1 = \"{\\\"$position\\\":{\\\"$reff\\\":21}}\";JSONObject json = JSONObject.parseObject(str1, Feature.DisableCircularReferenceDetect);System.out.println(json);}}结果:{\"$position\":{\"$reff\":21}}