alibaba fastjson 出现 $ref 循环引用问题解决

2023-10-20 13:28:17 update

这个问题是 alibaba 的 fastjson 库进行 JSON.toJSONString() 时出现的循环引用问题。

临时解决方案,使用时加上 SerializerFeature.DisableCircularReferenceDetect 的序列化特性。

String reqBody = JSONObject.toJSONString(cidto, SerializerFeature.DisableCircularReferenceDetect);

全局解决就是 new 一个类,在类中执行这个特性就行(类似于全局拦截器的理念),此处不给出了。


生成JSON字符串

来自你的消息:

java11,这个方法打印出来的 json string 为什么是这样的

the result json string is
{"aarch64":{"os_info":{"os_ver":"22.03","os_name":"dkk"}},"x86_64":{"$ref":"$.aarch64"}}
private void testJson() {
        String tmpArch = "x86_64,aarch64";
        boolean multiArch = tmpArch.contains(",");
        JSONObject osObj = new JSONObject();
        JSONObject infoObj = new JSONObject();
        infoObj.put("os_name", "dkk");
        infoObj.put("os_ver", "22.03");
        osObj.put("os_info", infoObj);
        //final String osObjStr = JSONObject.toJSONString(osObj);
        JSONObject customizedObj = new JSONObject();
        if (multiArch) {
            String[] archSplit = tmpArch.split(",");
            for (int i = 0; i < archSplit.length; i++) {
                String archItem = archSplit[i];
                //JSONObject tmpObj = JSONObject.parseObject(osObjStr);
                customizedObj.put(archItem, osObj);
            }
        }
        System.out.println("the result json string is\n" + JSONObject.toJSONString(customizedObj));
    }

来自 ChatGPT 的消息:

这是因为在testJson()方法中,首先创建了一个JSONObject对象osObj,并在其中放置了一个infoObj对象,该对象包含了os_nameos_ver两个属性。然后,如果tmpArch包含逗号,就将其拆分为多个架构,并将每个架构作为键,将osObj作为值放入customizedObj对象中。最后,使用JSONObject.toJSONString()方法将customizedObj对象转换为JSON字符串。

因此,在这个例子中,tmpArch包含x86_64aarch64,因此customizedObj对象包含两个属性,分别是x86_64aarch64x86_64的值是一个$ref属性,它引用了aarch64的值,而aarch64的值包含一个os_info属性,它又包含了os_nameos_ver两个属性。因此,最终的JSON字符串就是:

{
  "aarch64": {
    "os_info": {
      "os_ver": "22.03",
      "os_name": "dkk"
    }
  },
  "x86_64": {
    "$ref": "$.aarch64"
  }
}

来自你的消息:

为什么 x86_64 的值是一个 $ref 属性,并且这个属性的值是 $.aarch64

来自 ChatGPT 的消息:

在本次对话中,我们推测这个 JSON 字符串是使用 Jackson 库将一个 Java 对象转换成的。在这个 Java 对象中,可能存在一个类型为 Map<String, Object> 的属性,其中包含了两个键值对,分别是 x86_64aarch64。其中,x86_64 的值是 $ref 属性,它引用了 aarch64 的值。这是因为在 Java 对象中,x86_64aarch64 实际上是指向同一个对象的引用,因此在转换成 JSON 字符串时,x86_64 的值会被替换成 $ref 属性,指向 aarch64 的值。

$ref 属性的值为 $.aarch64,是因为在转换成 JSON 字符串时,Jackson 库会为每个对象生成一个唯一的标识符,用于标识该对象在 JSON 字符串中的位置。在这个 JSON 字符串中,aarch64 的值是一个对象,它包含了 os_info 属性,因此它的标识符为 $.aarch64。而 x86_64 的值是 $ref 属性,它引用了 aarch64 的值,因此它的标识符为 $.x86_64

来自你的消息:

private void testJson() {
        String tmpArch = "x86_64,aarch64";
        boolean multiArch = tmpArch.contains(",");
        JSONObject osObj = new JSONObject();
        JSONObject infoObj = new JSONObject();
        infoObj.put("os_name", "dkk");
        infoObj.put("os_ver", "22.03");
        osObj.put("os_info", infoObj);
        final String osObjStr = JSONObject.toJSONString(osObj);
        JSONObject customizedObj = new JSONObject();
        if (multiArch) {
            String[] archSplit = tmpArch.split(",");
            for (int i = 0; i < archSplit.length; i++) {
                String archItem = archSplit[i];
                JSONObject tmpObj = JSONObject.parseObject(osObjStr);
                customizedObj.put(archItem, tmpObj);
            }
        }
        System.out.println("the result json string is\n" + JSONObject.toJSONString(customizedObj));
    }

我这样写就能确保生成的 json string 正确

the result json string is
{"aarch64":{"os_info":{"os_ver":"22.03","os_name":"dkk"}},"x86_64":{"os_info":{"os_ver":"22.03","os_name":"dkk"}}}

来自 ChatGPT 的消息:

是的,您的代码能够确保生成的 JSON 字符串符合预期。在您的代码中,首先将 tmpArch 字符串按照逗号分隔成多个架构类型。然后,对于每个架构类型,创建一个新的 JSONObject 对象,并将 osObjStr 字符串解析成一个 JSONObject 对象,然后将这个对象作为当前架构类型的值,存入 customizedObj 对象中。最终,将 customizedObj 对象转换成 JSON 字符串并输出。

这样的代码逻辑确保了每个架构类型都有一个独立的 os_info 对象,而不是多个架构类型共享同一个 os_info 对象。因此,生成的 JSON 字符串中不会出现 $ref 属性,而是直接包含了所有的属性和值。

添加新评论