问题描述
在处理包含多态字段的对象时,JSON.toJSON() 存在缓存复用导致异常。
如果在同一 JVM 进程内,先对某子类实例调用了 JSON.toJSONString(),随后再对声明类型相同但实际类型不同的另一个子类实例调用 JSON.toJSON(),底层的 toJSONObject 会错误复用前者的 ObjectWriter 缓存,从而调用了不存在于当前实例的 getter 方法,抛出异常。
环境信息
- OS信息:Windows 11 amd64
- JDK信息:OpenJDK 21.0.10 (Temurin)
- 版本信息:Fastjson2 2.0.60
重现步骤
- 构建包含多态声明字段的对象。
- 先调用
JSON.toJSONString(element1) 触发底层缓存机制。
- 再调用
JSON.toJSON(element2)。
- 抛出类型不匹配异常:
invoke getter method error, attrText。
package com.example;
import com.alibaba.fastjson2.JSON;
public class BugReproduce {
public static class PropsValue {
private String commonField;
public String getCommonField() { return commonField; }
public void setCommonField(String commonField) { this.commonField = commonField; }
}
public static class TextPropsValue extends PropsValue {
private String attrText;
public String getAttrText() { return attrText; }
public void setAttrText(String attrText) { this.attrText = attrText; }
}
public static class ButtonPropsValue extends PropsValue {
private String attrButton;
public String getAttrButton() { return attrButton; }
public void setAttrButton(String attrButton) { this.attrButton = attrButton; }
}
public static class Element {
private PropsValue propsValue;
public PropsValue getPropsValue() { return propsValue; }
public void setPropsValue(PropsValue propsValue) { this.propsValue = propsValue; }
}
public static void main(String[] args) {
// 1. 构造第一个子类实例并触发缓存
TextPropsValue textValue = new TextPropsValue();
textValue.setCommonField("common");
textValue.setAttrText("hello");
Element element1 = new Element();
element1.setPropsValue(textValue);
// 走 write 路径,写入 TextPropsValue 的缓存
String json1 = JSON.toJSONString(element1);
// 备注:调用 JSON.toJSON(element1, JSONWriter.Feature.ReferenceDetection) 同样会触发后续污染
// 2. 构造第二个子类实例
ButtonPropsValue buttonValue = new ButtonPropsValue();
buttonValue.setCommonField("common");
buttonValue.setAttrButton("click");
Element element2 = new Element();
element2.setPropsValue(buttonValue);
// 3. 再次调用,触发异常
Object json2 = JSON.toJSON(element2);
}
}
期待的正确结果
预期第二次调用不抛异常。对于多态字段,应基于当前字段值的运行时真实类型选择正确的 ObjectWriter,在发现缓存绑定的类型与实际类型不符时,应当跳过该缓存并重新动态获取 ObjectWriter。
相关日志输出
Exception in thread "main" com.alibaba.fastjson2.JSONException: invoke getter method error, attrText
at com.alibaba.fastjson2.writer.FieldWriterStringMethod.getFieldValue(FieldWriterStringMethod.java:29)
at com.alibaba.fastjson2.writer.ObjectWriterAdapter.toJSONObject(ObjectWriterAdapter.java:618)
at com.alibaba.fastjson2.writer.ObjectWriterAdapter.toJSONObject(ObjectWriterAdapter.java:610)
at com.alibaba.fastjson2.writer.ObjectWriterAdapter.toJSONObject(ObjectWriterAdapter.java:691)
at com.alibaba.fastjson2.JSON.toJSON(JSON.java:4027)
at com.alibaba.fastjson2.JSON.toJSON(JSON.java:3999)
at com.example.BugReproduce.main(BugReproduce.java:55)
Caused by: java.lang.IllegalArgumentException: object is not an instance of declaring class
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.checkReceiver(DirectMethodHandleAccessor.java:197)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:99)
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
at com.alibaba.fastjson2.writer.FieldWriterStringMethod.getFieldValue(FieldWriterStringMethod.java:27)
... 6 more
附加信息
如果你还有其他需要提供的信息,可以在这里填写(可以提供截图、视频等)。
问题描述
在处理包含多态字段的对象时,JSON.toJSON() 存在缓存复用导致异常。
如果在同一 JVM 进程内,先对某子类实例调用了 JSON.toJSONString(),随后再对声明类型相同但实际类型不同的另一个子类实例调用 JSON.toJSON(),底层的 toJSONObject 会错误复用前者的 ObjectWriter 缓存,从而调用了不存在于当前实例的 getter 方法,抛出异常。
环境信息
重现步骤
JSON.toJSONString(element1)触发底层缓存机制。JSON.toJSON(element2)。invoke getter method error, attrText。期待的正确结果
预期第二次调用不抛异常。对于多态字段,应基于当前字段值的运行时真实类型选择正确的 ObjectWriter,在发现缓存绑定的类型与实际类型不符时,应当跳过该缓存并重新动态获取 ObjectWriter。
相关日志输出
附加信息
如果你还有其他需要提供的信息,可以在这里填写(可以提供截图、视频等)。