先說結論:
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}}