Skip to content

[BUG]JSON.toJSON() 在多态字段场景复用 ObjectWriter 缓存未校验类型,导致 invoke getter method error #7638

@Aspiriniii

Description

@Aspiriniii

问题描述

在处理包含多态字段的对象时,JSON.toJSON() 存在缓存复用导致异常。
如果在同一 JVM 进程内,先对某子类实例调用了 JSON.toJSONString(),随后再对声明类型相同但实际类型不同的另一个子类实例调用 JSON.toJSON(),底层的 toJSONObject 会错误复用前者的 ObjectWriter 缓存,从而调用了不存在于当前实例的 getter 方法,抛出异常。

环境信息

  • OS信息:Windows 11 amd64
  • JDK信息:OpenJDK 21.0.10 (Temurin)
  • 版本信息:Fastjson2 2.0.60

重现步骤

  1. 构建包含多态声明字段的对象。
  2. 先调用 JSON.toJSONString(element1) 触发底层缓存机制。
  3. 再调用 JSON.toJSON(element2)
  4. 抛出类型不匹配异常: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

附加信息

如果你还有其他需要提供的信息,可以在这里填写(可以提供截图、视频等)。

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions