[BUG]在fastjson1兼容包中setAutoTypeSupport(true)不生效
- Dominant language
- Java
- Stars
- 4.4k
- Forks
- 613
- Avg merge
- 1d 22h
- Merged PRs (30d)
- 6
Description
### 问题描述
在从fastjson1迁移到fastjson2时,我们使用以下兼容包来支持一些外部组件
```
com.alibaba
fastjson
2.0.62
```
但在某第三方组件中,有以下用法(先用 `WriteClassName` 写入带 `@type` 的 JSON,再通过自定义 `ParserConfig` 开启 autoType 读回原类型):
```java
ParserConfig config = new ParserConfig();
config.setSafeMode(false);
config.setAutoTypeSupport(true); // fastjson1 语义:开启 @type 自动类型还原
// 写入(第三方组件原逻辑)
String json = JSON.toJSONString(obj, SerializerFeature.WriteClassName);
// 读取(第三方组件原逻辑)
Object o = JSON.parse(json, config);
```
在 fastjson1(1.2.x)中,`JSON.parse(String, ParserConfig)` 会按 `@type` 还原出原始类型;
但在兼容包(2.0.56 / 2.0.62 均复现)中,**`setAutoTypeSupport(true)` 完全不生效**,`JSON.parse` 一律返回 `com.alibaba.fastjson.JSONObject`,上层强转原始类型时抛出 `ClassCastException`。
根因(对兼容包反编译确认):
1. **兼容层 `ParserConfig.setAutoTypeSupport(boolean)` 是死字段**:仅赋值给自身私有字段 `autoTypeSupport`,未透传给 fastjson2 的 `ObjectReaderProvider`,且在 `JSON.parse` 调用路径上无任何代码读取该字段:
```java
// com.alibaba.fastjson.parser.ParserConfig(兼容包 2.0.56/2.0.62 字节码一致)
public void setAutoTypeSupport(boolean autoTypeSupport) {
this.autoTypeSupport = autoTypeSupport; // 只写字段,无人读取
}
```
2. **兼容层 `JSON.parse(String, ParserConfig)` 根本不使用该字段**:只取 `config.getProvider()`,且 `createReadContext` 使用固定的 `DEFAULT_PARSER_FEATURE`(不含 `SupportAutoType`);`JSONFactory.defaultReaderFeatures` 恒为 0 且没有 setter。于是 `isSupportAutoType(0)` 恒为 `false`,直接按 `JSONObject` 解析、忽略 `@type`:
```java
// com.alibaba.fastjson.JSON#parse(String, ParserConfig)(兼容包反编译)
JSONReader.Context ctx = createReadContext(config.getProvider(), DEFAULT_PARSER_FEATURE, new Feature[0]);
try (JSONReader reader = JSONReader.of(text, ctx)) {
if (reader.isObject() && !reader.isSupportAutoType(0L)) { // 恒为 true
return reader.read(JSONObject.class); // 直接读成 JSONObject,@type 被忽略
}
return reader.readAny();
}
```
即:在兼容包下,该路径**没有任何方法**可以让 `@type` 被还原(无配置开关可解),所有依赖 `WriteClassName` 写入 + `JSON.parse(String, ParserConfig)` 读回的老代码都会失败。
### 环境信息
*请填写以下信息:*
- OS信息:Windows 11 / Linux(均可复现,与 OS 无关)
- JDK信息:dragonwell-17.0.18(OpenJDK 17)
- 版本信息:Fastjson 兼容包 2.0.56、2.0.62(均复现);对照组 fastjson 1.2.83 行为正常
### 重现步骤
使用上述问题描述中的写法,编译运行以下最小复现代码:
```java
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
import com.alibaba.fastjson.serializer.SerializerFeature;
public class AutoTypeCompatRepro {
public static class Session {
private String id;
private String host;
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public String getHost() { return host; }
public void setHost(String host) { this.host = host; }
}
public static void main(String[] args) {
ParserConfig config = new ParserConfig();
config.setSafeMode(false);
config.setAutoTypeSupport(true); // fastjson1 语义:开启 @type 自动类型还原
Session s = new Session();
s.setId("session-001");
s.setHost("127.0.0.1");
String json = JSON.toJSONString(s, SerializerFeature.WriteClassName);
System.out.println("write => " + json);
Object o = JSON.parse(json, config);
System.out.println("read => " + o.getClass().getName());
// fastjson1(1.2.x) 返回: AutoTypeCompatRepro$Session(@type 正常还原)
// 兼容包(2.0.x) 实际返回: com.alibaba.fastjson.JSONObject
// 随后强转目标类型即抛 ClassCastException
Session restored = (Session) o;
System.out.println("id=" + restored.getId());
}
}
```
实测输出:
fastjson 1.2.83(对照组,行为正常):
```
write => {"@type":"AutoTypeCompatRepro$Session","host":"127.0.0.1","id":"session-001"}
read => AutoTypeCompatRepro$Session
id=session-001
```
fastjson 2.0.56 / 2.0.62(兼容包,均复现):
```
write => {"@type":"AutoTypeCompatRepro$Session","host":"127.0.0.1","id":"session-001"}
read => com.alibaba.fastjson.JSONObject
Exception in thread "main" java.lang.ClassCastException: class com.alibaba.fastjson.JSONObject cannot be cast to class AutoTypeCompatRepro$Session
```
### 期待的正确结果
兼容包中,`JSON.parse(String, ParserConfig)` 应尊重 `ParserConfig.setAutoTypeSupport(true)` 的 fastjson1 语义:将 `autoTypeSupport` 透传到 fastjson2 的 `ObjectReaderProvider`(或在 `createReadContext` 中按该字段为 `JSONReader.Context` 加上 `JSONReader.Feature.SupportAutoType`),使 `@type` 能正常还原,与 fastjson1(1.2.x)行为保持一致。
### 相关日志输出
现场真实环境报错(第三方组件 bizframe 的定时清理任务,Redis 中 session 数据为 fastjson1 写入的带 `@type` JSON):
```
2024-12-25 16:45:05.034 |-WARN [UserRightInfoClearTask-140] com.hundsun.jres.bizframe.schedule.UserRightInfoClearTask [] -| UserRightInfoClearTask clear Exception
java.lang.ClassCastException: class com.alibaba.fastjson.JSONObject cannot be cast to class com.hundsun.jrescloud.bizframe3.security.session.Session (com.alibaba.fastjson.JSONObject and com.hundsun.jrescloud.bizframe3.security.session.Session are in unnamed module of loader org.springframework.boot.loader.launch.LaunchedClassLoader @4ae33a11)
at com.hundsun.jres.bizframe.schedule.UserRightInfoClearTask.clearUserRightInfo(UserRightInfoClearTask.java:149)
at com.hundsun.jres.bizframe.schedule.UserRightInfoClearTask.clearNoException(UserRightInfoClearTask.java:110)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:539)
at java.base/java.util.concurrent.FutureTask.runAndReset(FutureTask.java:305)
at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:305)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
at java.base/java.lang.Thread.run(Thread.java:840)
```
第三方组件反序列化代码(`com.hundsun.jres.bizframe.cache.auth.AuthCacheAdapter#asObject`):
```java
public T asObject(String content) {
return (T) JSON.parse(content, this.config); // config 已 setSafeMode(false) + setAutoTypeSupport(true)
}
```
#### 附加信息
1. fastjson2 内核本身能力正常:使用 fastjson2 原生 API `com.alibaba.fastjson2.JSON.parseObject(content, Object.class, JSONReader.Feature.SupportAutoType)` 可以正确还原 `@type`(已实测验证)。说明缺陷仅在兼容层未将 `ParserConfig.autoTypeSupport` 接线到 fastjson2。
2. 兼容层 `ParserConfig` 的 `addAccept(String)` / `addDeny(String)` 已正确透传到 `ObjectReaderProvider.addAutoTypeAccept/addAutoTypeDeny`,唯独 `setAutoTypeSupport` 是死字段,推测为移植遗漏。
3. 本 issue 与之前反馈的 `ObjectWriterAdapter.toJSONObject` 复用 `FieldWriterObject` 缓存 writer 未做类型校验的问题相互独立,是两个不同的兼容层/内核缺陷。
Contributor guide
Research direction
Start with the compatibility-layer ParserConfig.setAutoTypeSupport(boolean) and JSON.parse(String, ParserConfig) paths described in the issue, then compare them with native fastjson2 parsing using JSONReader.Feature.SupportAutoType. Add a regression test using the supplied WriteClassName and Session example; done means setAutoTypeSupport(true) restores the typed object instead of returning JSONObject.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java
- Domain
- backend
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 72/100