[BUG]循环引用bug
- Dominant language
- Java
- Stars
- 4.4k
- Forks
- 613
- Avg merge
- 1d 22h
- Merged PRs (30d)
- 6
Description
### 问题描述
*简要描述您碰到的问题。*
升级2.X api版本默认以及api不兼容,不符合JSON规范输出
### 环境信息
*请填写以下信息:*
- OS信息: [e.g.:CentOS 8.4.2105 4Core 3.10GHz 16 GB]
- JDK信息: [e.g.:Openjdk 1.8.0_312]
- 版本信息:[e.g.:Fastjson2 2.x.x]
之前是1.2.83版本
com.alibaba
fastjson
2.0.64
### 重现步骤
*如何操作可以重现该问题:*
1. 使用 `xxx.xxx` 方法
2. 输入 `...` 数据
3. 出现 `...` 错误
```java
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* FastJSON2 循环引用 / $ref 场景自测。
*
* IDEA:右键 Run {@link #main(String[])}。
* 关注控制台 JSON 里是否出现 {@code "$ref"}。
*/
public class Fastjson2RefScenariosTest {
public static void main(String[] args) {
System.out.println("fastjson version = " + JSON.VERSION);
System.out.println("--------------------------------------------------");
run("1. 共享 Integer", sharedInteger(), false);
run("2. 共享 String", sharedString(), false);
run("3. Map 两 key 同一 List", mapSharedList(), true);
run("4. Map 两 key 同一 Map 实例", mapSharedMap(), true);
run("5. LimitGroup POJO + 共享 List(插件 getLimitGroup)", pojoSharedList(), false);
run("6. LimitGroup 共享 Set", flashSharedSet(), false);
run("7. getAttr:多次 JSON.toJSON(dataMap)", getAttrStyleJsonToJson(), false);
run("8. getAttr:同一 Map 实例放两 key", sameMapTwoKeys(), true);
run("9. limit 用 Map 结构 + 共享 List", mapLimitSharedList(), true);
run("10. 完整 Req:attr 共享 Map + limit POJO", fullReq(), true);
run("11. 同一 JSONObject 放两 key", sharedJSONObject(), true);
run("12. 数组两元素同一 List", arraySameObject(), false);
run("13. 真循环 parent<->child", circular(), true);
System.out.println("--------------------------------------------------");
System.out.println("DisableCircularReferenceDetect 修复示例(场景 3):");
String fixed = JSON.toJSONString(mapSharedList(), SerializerFeature.DisableCircularReferenceDetect);
System.out.println(fixed);
System.out.println("has $ref: " + fixed.contains("$ref"));
}
private static void run(String title, Object obj, boolean expectRef) {
String json = JSON.toJSONString(obj);
boolean hasRef = json.contains("$ref");
String mark = hasRef == expectRef ? "OK" : "UNEXPECTED";
System.out.println("\n=== " + title + " [" + mark + ", expect $ref=" + expectRef + "] ===");
System.out.println(json);
System.out.println("has $ref: " + hasRef);
}
private static Map sharedInteger() {
Integer v = Integer.valueOf(6);
Map m = new LinkedHashMap<>();
m.put("a", v);
m.put("b", v);
return m;
}
private static Map sharedString() {
String v = "CN";
Map m = new LinkedHashMap<>();
m.put("a", v);
m.put("b", v);
return m;
}
private static Map mapSharedList() {
List list = new ArrayList<>();
list.add("-1");
Map m = new LinkedHashMap<>();
m.put("area", list);
m.put("macs", list);
return m;
}
private static Map mapSharedMap() {
Map inner = new LinkedHashMap<>();
inner.put("data", new ArrayList());
Map m = new LinkedHashMap<>();
m.put("areaList", inner);
m.put("macs", inner);
return m;
}
private static Req pojoSharedList() {
List list = new ArrayList<>();
list.add("-1");
List rules = new ArrayList<>();
Rule r1 = new Rule();
r1.ruleName = "area";
r1.ruleType = 1;
r1.value = list;
Rule r2 = new Rule();
r2.ruleName = "macs";
r2.ruleType = 1;
r2.value = list;
rules.add(r1);
rules.add(r2);
LimitGroup g = new LimitGroup();
g.groupName = "DEFAULT";
g.rules = rules;
Req req = new Req();
req.operatorId = "PLUGIN";
req.limit = Collections.singletonList(g);
return req;
}
private static Req flashSharedSet() {
Set areas = new LinkedHashSet<>(Arrays.asList("CN", "IN"));
List groups = new ArrayList<>();
for (int i = 0; i < 2; i++) {
List rules = new ArrayList<>();
Rule area = new Rule();
area.ruleName = "publishArea";
area.ruleType = 1;
area.value = areas;
Rule other = new Rule();
other.ruleName = i == 0 ? "marketModel" : "versionPackageName";
other.ruleType = 6;
other.value = "CPH2173";
rules.add(area);
rules.add(other);
LimitGroup g = new LimitGroup();
g.groupName = "NUM_" + i;
g.rules = rules;
groups.add(g);
}
Req req = new Req();
req.operatorId = "grs";
req.limit = groups;
return req;
}
private static Map getAttrStyleJsonToJson() {
HashMap dataMap = new HashMap<>();
dataMap.put("data", new ArrayList());
HashMap resultMap = new HashMap<>();
resultMap.put("pluginCode", JSON.toJSON(dataMap));
dataMap.put("data", "v1");
resultMap.put("version", JSON.toJSON(dataMap));
return resultMap;
}
private static Map sameMapTwoKeys() {
Map dataMap = new HashMap<>();
dataMap.put("data", new ArrayList());
Map resultMap = new LinkedHashMap<>();
resultMap.put("areaList", dataMap);
resultMap.put("macs", dataMap);
return resultMap;
}
private static Map mapLimitSharedList() {
List list = new ArrayList<>();
list.add("-1");
Map areaRule = new LinkedHashMap<>();
areaRule.put("ruleName", "area");
areaRule.put("value", list);
Map macRule = new LinkedHashMap<>();
macRule.put("ruleName", "macs");
macRule.put("value", list);
Map group = new LinkedHashMap<>();
group.put("groupName", "DEFAULT");
group.put("rules", Arrays.asList(areaRule, macRule));
Map req = new LinkedHashMap<>();
req.put("limit", Collections.singletonList(group));
return req;
}
private static Req fullReq() {
Req req = pojoSharedList();
req.attr = sameMapTwoKeys();
return req;
}
private static Map sharedJSONObject() {
JSONObject jo = new JSONObject();
jo.put("data", new ArrayList());
Map m = new LinkedHashMap<>();
m.put("areaList", jo);
m.put("macs", jo);
return m;
}
private static Map arraySameObject() {
List list = new ArrayList<>();
list.add("CN");
Map m = new LinkedHashMap<>();
m.put("items", Arrays.asList(list, list));
return m;
}
private static Map circular() {
Map parent = new LinkedHashMap<>();
Map child = new LinkedHashMap<>();
parent.put("name", "p");
parent.put("child", child);
child.put("name", "c");
child.put("parent", parent);
return parent;
}
static class Rule {
public String ruleName;
public int ruleType;
public Object value;
}
static class LimitGroup {
public String groupName;
public List rules;
}
static class Req {
public String operatorId;
public Object limit;
public Map attr;
}
}
```
### 期待的正确结果
*对您期望发生的结果进行清晰简洁的描述。*
希望对于引用这类在java中那是百分之百存在的;但是fastjson却默认序列化有问题;导致传递给下游字段值是$.ref
### 相关日志输出
*请复制并粘贴任何相关的日志输出。*
#### 附加信息
*如果你还有其他需要提供的信息,可以在这里填写(可以提供截图、视频等)。*
非常感谢项目开源奉献,历史旧项目改不动只能升级,但希望项目开源版本前使用AI自测各种API兼容性;不兼容的能明确告知,输出明确差异点;不要开源后拿用户当测试;
Contributor guide
Research direction
Start with the supplied Fastjson2RefScenariosTest.main entry point and run its scenarios against Fastjson 2.0.64, comparing the observed output with the reported Fastjson 1.2.83 behavior. The issue needs a decided compatibility expectation for shared references versus true circular references, with the relevant differences and intended JSON output documented before implementation.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java
- Domain
- api, backend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100