[BUG] JSONReader.Feature.SupportSmartMatch使用后会导致中相同类的解析崩溃(非常诡异)
问题描述
在使用过 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)
附加信息
可以在步骤二、步骤三中间清理一下HolderVO反序列化器的缓存,因为你这里s和upCaseS1 生成的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);
可以在步骤二、步骤三中间清理一下
HolderVO反序列化器的缓存,因为你这里s和upCaseS1生成的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都要做一次缓存清理嘛?我能理解清理的作用,但是在整个应用中使用了这个配置,会影响应用中其他线程或者模块应用的反序列化,是否不太合适?
是这样的,框架底层使用 asm 创建反序列化器时(同时开启了 SupportSmartMatch)处理机制不太完善:
- 步骤一中,为 HolderVOTest 创建反序列化器并放入了全局缓存,此时解析器知道 manager 字段的值是一个内嵌JSON的字符串;
- 步骤二,启用 SmartMatch 时,修改了缓存的解析器实例,把处理 manager 字段的逻辑改成了期望一个JSON对象;
- 然后步骤三,仍然使用上述缓存的解析器,它期待 manager 是一个 JSON 对象,结果是 " 开头的字符串,所以报错。
这里修复的话,可能较麻烦,您可以①暂时在应用启动时使用 -Dfastjson2.creator=reflect 来避开上述问题(经测试无问题);②确保两个 manager 的类型一致。
是这样的,框架底层使用 asm 创建反序列化器时(同时开启了 SupportSmartMatch)处理机制不太完善:
- 步骤一中,为 HolderVOTest 创建反序列化器并放入了全局缓存,此时解析器知道 manager 字段的值是一个内嵌JSON的字符串;
- 步骤二,启用 SmartMatch 时,修改了缓存的解析器实例,把处理 manager 字段的逻辑改成了期望一个JSON对象;
- 然后步骤三,仍然使用上述缓存的解析器,它期待 manager 是一个 JSON 对象,结果是 " 开头的字符串,所以报错。
这里修复的话,可能较麻烦,您可以①暂时在应用启动时使用 -Dfastjson2.creator=reflect 来避开上述问题(经测试无问题);②确保两个 manager 的类型一致。
修复麻烦可以理解,从工程角度讲,SupportSmartMatch缓存处理机制存在问题是否应该默认不开启,因此影响整个项目的处理其实是很危险的(虽然我的issue中的处理使用方式不是很常见)
一般情况下,开启 SupportSmartMatch 后只是追加了智能匹配的功能,并不影响 非SmartMatch 的功能。但是这里情况比较特殊