alibaba / alibaba/fastjson2

[BUG] com.alibaba.fastjson.JSON.toJSON 方法在属性声明为父类时丢失子类属性

Open
#7,655 0 comments 0 reactions 0 assignees View on GitHub
bug
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.