fastjson2 icon indicating copy to clipboard operation
fastjson2 copied to clipboard

[BUG] JSONReader.Feature.SupportSmartMatch使用后会导致中相同类的解析崩溃(非常诡异)

Open mystox opened this issue 1 month ago • 5 comments

问题描述

在使用过 JSONReader.Feature.SupportSmartMatch 后,整个应用中所有该类有关的的toJavaList都会报错,现象参考代码

重现步骤

    public static void main(String[] args) {
        String s = "[{\"manager\":\"{\\\"certNo\\\":\\\"11\\\",\\\"name\\\":\\\"test\\\"}\"}]";

        String upCaseS1 = "{\"MANAGER\":{\"certNo\":\"11\",\"name\":\"test\"}}";

        //步骤一 正常解析
        com.alibaba.fastjson2.JSONArray data1 = com.alibaba.fastjson2.JSONArray.parseArray(s);
        List<HolderVOTest> javaList1 = data1.toJavaList(HolderVOTest.class);
        System.out.println(javaList1);

        //步骤二 使用配置后
        HolderVOTest holderVO1 = JSON.parseObject(upCaseS1, HolderVOTest.class, JSONReader.Feature.SupportSmartMatch);
        System.out.println(holderVO1);

        //步骤三,解析出错
        com.alibaba.fastjson2.JSONArray data = com.alibaba.fastjson2.JSONArray.parseArray(s);
        List<HolderVOTest> javaList = data.toJavaList(HolderVOTest.class);
        System.out.println(javaList);
    }

public class HolderVOTest {

    private HolderDTOTest manager;

    public HolderDTOTest getManager() {
        return manager;
    }

    public void setManager(HolderDTOTest manager) {
        this.manager = manager;
    }

    @Override
    public String toString() {
        return "HolderVOTest{" +
                "manager=" + manager +
                '}';
    }
}
public class HolderDTOTest {

    private String name;


    private String certNo;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getCertNo() {
        return certNo;
    }

    public void setCertNo(String certNo) {
        this.certNo = certNo;
    }

    @Override
    public String toString() {
        return "HolderDTOTest{" +
                "name='" + name + '\'' +
                ", certNo='" + certNo + '\'' +
                '}';
    }

期待的正确结果

可以正常解析,诡异的是整个应用的相关解析都会报错,无论是哪个线程

相关日志输出

[HolderVOTest{manager=HolderDTOTest{name='test', certNo='11'}}] HolderVOTest{manager=HolderDTOTest{name='test', certNo='11'}} Exception in thread "main" com.alibaba.fastjson2.JSONException: expect ':', but , offset 1, character ", line 1, column 1, fastjson-version 2.0.60 "{"certNo":"11","name":"test"}" at com.alibaba.fastjson2.JSONReaderUTF16.readFieldNameHashCode(JSONReaderUTF16.java:1417) at com.alibaba.fastjson2.reader.ORG_2_2_HolderDTOTest.readObject(Unknown Source) at com.alibaba.fastjson2.reader.FieldReader.acceptAny(FieldReader.java:468) at com.alibaba.fastjson2.reader.ObjectReaderAdapter.createInstance(ObjectReaderAdapter.java:736) at com.alibaba.fastjson2.JSONArray.toList(JSONArray.java:1203) at com.alibaba.fastjson2.JSONArray.toJavaList(JSONArray.java:1292) at tech.mystox.test.fastjson.HolderVOTest.main(HolderVOTest.java:34)

附加信息

mystox avatar Nov 14 '25 09:11 mystox

可以在步骤二、步骤三中间清理一下HolderVO反序列化器的缓存,因为你这里supCaseS1 生成的HolderVO反序列化器不一样,导致问题

        //步骤二 使用配置后
        HolderVOTest holderVO1 = JSON.parseObject(upCaseS1, HolderVOTest.class, JSONReader.Feature.SupportSmartMatch);
        System.out.println(holderVO1);

        JSONFactory.getDefaultObjectReaderProvider().cleanup(HolderVOTest.class); // 加上这条,清理缓存

        //步骤三,解析出错
        JSONArray data = JSONArray.parseArray(s);
        List<HolderVOTest> javaList = data.toJavaList(HolderVOTest.class);
        System.out.println(javaList);

jujn avatar Nov 15 '25 05:11 jujn

可以在步骤二、步骤三中间清理一下HolderVO反序列化器的缓存,因为你这里supCaseS1 生成的HolderVO反序列化器不一样,导致问题

        //步骤二 使用配置后
        HolderVOTest holderVO1 = JSON.parseObject(upCaseS1, HolderVOTest.class, JSONReader.Feature.SupportSmartMatch);
        System.out.println(holderVO1);

        JSONFactory.getDefaultObjectReaderProvider().cleanup(HolderVOTest.class); // 加上这条,清理缓存

        //步骤三,解析出错
        JSONArray data = JSONArray.parseArray(s);
        List<HolderVOTest> javaList = data.toJavaList(HolderVOTest.class);
        System.out.println(javaList);

em、原则上每次在使用过JSONReader.Feature.SupportSmartMatch都要做一次缓存清理嘛?我能理解清理的作用,但是在整个应用中使用了这个配置,会影响应用中其他线程或者模块应用的反序列化,是否不太合适?

mystox avatar Nov 16 '25 13:11 mystox

是这样的,框架底层使用 asm 创建反序列化器时(同时开启了 SupportSmartMatch)处理机制不太完善:

  • 步骤一中,为 HolderVOTest 创建反序列化器并放入了全局缓存,此时解析器知道 manager 字段的值是一个内嵌JSON的字符串;
  • 步骤二,启用 SmartMatch 时,修改了缓存的解析器实例,把处理 manager 字段的逻辑改成了期望一个JSON对象;
  • 然后步骤三,仍然使用上述缓存的解析器,它期待 manager 是一个 JSON 对象,结果是 " 开头的字符串,所以报错。

这里修复的话,可能较麻烦,您可以①暂时在应用启动时使用 -Dfastjson2.creator=reflect 来避开上述问题(经测试无问题);②确保两个 manager 的类型一致。

jujn avatar Nov 17 '25 03:11 jujn

是这样的,框架底层使用 asm 创建反序列化器时(同时开启了 SupportSmartMatch)处理机制不太完善:

  • 步骤一中,为 HolderVOTest 创建反序列化器并放入了全局缓存,此时解析器知道 manager 字段的值是一个内嵌JSON的字符串;
  • 步骤二,启用 SmartMatch 时,修改了缓存的解析器实例,把处理 manager 字段的逻辑改成了期望一个JSON对象;
  • 然后步骤三,仍然使用上述缓存的解析器,它期待 manager 是一个 JSON 对象,结果是 " 开头的字符串,所以报错。

这里修复的话,可能较麻烦,您可以①暂时在应用启动时使用 -Dfastjson2.creator=reflect 来避开上述问题(经测试无问题);②确保两个 manager 的类型一致。

修复麻烦可以理解,从工程角度讲,SupportSmartMatch缓存处理机制存在问题是否应该默认不开启,因此影响整个项目的处理其实是很危险的(虽然我的issue中的处理使用方式不是很常见)

mystox avatar Nov 18 '25 03:11 mystox

一般情况下,开启 SupportSmartMatch 后只是追加了智能匹配的功能,并不影响 非SmartMatch 的功能。但是这里情况比较特殊

jujn avatar Nov 18 '25 03:11 jujn