[BUG] com.alibaba.fastjson.JSON.toJSON 方法在属性声明为父类时丢失子类属性
- Dominant language
- Java
- Stars
- 4.4k
- Forks
- 613
- Avg merge
- 1d 22h
- Merged PRs (30d)
- 6
Description
### 问题描述
在 fastjson2 的 com.alibaba:fastjson 兼容包中,使用 com.alibaba.fastjson.JSON.toJSON(Object) 方法将一个复合对象转换为 JSONObject 时,如果某个属性的声明类型(编译期类型)是父类,但实际运行期类型是子类,那么子类特有的属性会在转换过程中丢失。
该问题似乎是因为兼容层的序列化逻辑严格依据了字段的声明类型(Compile-time Type)来解析 Bean 属性,而没有动态识别运行期的实际多态类型(Runtime Type)。
### 环境信息
- OS信息: Any
- JDK信息: JDK21
- 版本信息:>= 2.0.57
### 重现步骤
```java
package com.example.demo;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
public class Fastjson2IssueTest {
public static void main(String[] args) {
// 1. 创建子类 ClassB 对象并赋值
ClassB classB = new ClassB();
classB.setField1("value1");
classB.setField2("value2");
classB.setField3("value3");
classB.setField4("value4");
// 2. 创建 ClassC 对象(其内部属性声明为父类 ClassA)
ClassC classC = new ClassC();
classC.setFieldClassA(classB); // 将子类实例赋给父类声明的字段
// 3. 通过 fastjson 1.x 兼容接口转换为 JSONObject
JSONObject json1 = (JSONObject) JSON.toJSON(classC);
// 4. 输出结果
System.out.println("转换后的 JSON 字符串: " + json1.toJSONString());
// 5. 验证子类属性是否存在
JSONObject fieldClassAJson = json1.getJSONObject("fieldClassA");
if (!fieldClassAJson.containsKey("field3") || !fieldClassAJson.containsKey("field4")) {
System.err.println("❌ BUG 触发: 子类特有属性 'field3' 或 'field4' 丢失了!");
} else {
System.out.println("✅ 验证通过: 属性完整。");
}
}
// --- 测试实体类 ---
public static class ClassA {
private String field1;
private String field2;
public String getField1() { return field1; }
public void setField1(String field1) { this.field1 = field1; }
public String getField2() { return field2; }
public void setField2(String field2) { this.field2 = field2; }
}
public static class ClassB extends ClassA {
private String field3;
private String field4;
public String getField3() { return field3; }
public void setField3(String field3) { this.field3 = field3; }
public String getField4() { return field4; }
public void setField4(String field4) { this.field4 = field4; }
}
public static class ClassC {
// 声明类型为父类 ClassA
private ClassA fieldClassA;
public ClassA getFieldClassA() { return fieldClassA; }
public void setFieldClassA(ClassA fieldClassA) { this.fieldClassA = fieldClassA; }
}
}
```
### 期望结果
由于实际传入的是 ClassB 实例,转换后的 JSON 应该保留子类的属性(即包含 field3 和 field4):
```json
{
"fieldClassA": {
"field1": "value1",
"field2": "value2",
"field3": "value3",
"field4": "value4"
}
}
```
### 实际结果
子类 ClassB 的特有属性被裁剪掉,仅剩下了父类 ClassA 的属性:
```
{
"fieldClassA": {
"field1": "value1",
"field2": "value2"
}
}
```
#### 附加信息
实际测试2.0.54没有问题,2.0.57、5.0.61、2.0.62试了都有问题
Contributor guide
Research direction
Start with the com.alibaba.fastjson.JSON.toJSON(Object) compatibility entry point and run the supplied ClassA/ClassB/ClassC reproduction on an affected version. Trace how the declared field type is used during conversion, then verify that a runtime ClassB value retains field3 and field4 in the resulting 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
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 62/100