

FastJSON字段智能匹配踩坑
source link: https://wakzz.cn/2021/01/01/java/FastJSON%E5%AD%97%E6%AE%B5%E6%99%BA%E8%83%BD%E5%8C%B9%E9%85%8D%E8%B8%A9%E5%9D%91/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

FastJSON字段智能匹配踩坑
2021年第一天早上,客户突然投诉说系统的一个功能出了问题,紧急排查后发现后端系统确实出了bug,原因为前端传输的JSON报文,后端反序列化成JavaBean后部分字段的值丢失了。
查看git提交历史记录,前端和后端近期并未对该功能的接口字段做任何修改,联想到上个版本升级了后端的FastJSON的版本,怀疑是后端系统对FastJSON升级导致的问题。
@Data
static class Label {
@JSONField(name = "label_id")
private Integer labelId;
private String labelName;
}
public static void main(String[] args) {
String value = "{'labelId': 1,'label_name':'name'}";
Label label = JSON.parseObject(value, Label.class);
System.out.println(label);
}
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.60</version>
</dependency>
使用低版本FastJSON,如上使用1.2.60
版本,示例输出的结果如下,即两个字段JSON解析映射成功。虽然JavaBean中的字段和JSON中的key并不完全匹配(大小写不匹配以及下划线匹配),但得益于FastJSON的智能匹配,忽略了大小写和下划线,依然将JSON映射成功。
Label(labelId=1, labelName=name)
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.71</version>
</dependency>
使用高版本FastJSON,如上使用1.2.71
,示例输出结果如下,字段labelId
映射失败,即高版本FastJSON对智能匹配规则做了修改,并且未向前兼容而导致了部分字段映射失败导致了这次的bug。
Label(labelId=null, labelName=name)
解析高版本FastJSON字段智能匹配失败的原因,首先要先了解智能匹配的规则。
低版本的智能匹配规则的关键代码如下,翻译成人话就是:
- 如果JavaBean字段有
@JSONField
注解且name不空时,则对name的值忽略字母大小写和-
,_
两个字符 - 否则取JavaBean的字段名,忽略字母大小写和
-
,_
两个字符 - JSON中的key忽略
is
开头并忽略剩余字母大小写和-
,_
两个字符
// 对JSON中没有成功映射JavaBean的key做智能匹配
// 1. 忽略key的字母大小写和'-','_'两个字符
long smartKeyHash = TypeUtils.fnv1a_64_lower(key);
if (this.smartMatchHashArray == null) {
long[] hashArray = new long[sortedFieldDeserializers.length];
for (int i = 0; i < sortedFieldDeserializers.length; i++) {
// fieldInfo.name优先取@JSONField的name字段,其次取JavaBean字段名
// fieldInfo.name忽略字母大小写和'-','_'两个字符尝试与JSON中的key做智能匹配
hashArray[i] = TypeUtils.fnv1a_64_lower(sortedFieldDeserializers[i].fieldInfo.name);
}
Arrays.sort(hashArray);
this.smartMatchHashArray = hashArray;
}
int pos = Arrays.binarySearch(smartMatchHashArray, smartKeyHash);
// 2. 如果key以'is'开头,则忽略'is'开头并忽略剩余字母大小写和'-','_'两个字符
boolean is = false;
if (pos < 0 && (is = key.startsWith("is"))) {
smartKeyHash = TypeUtils.fnv1a_64_lower(key.substring(2));
pos = Arrays.binarySearch(smartMatchHashArray, smartKeyHash);
}
高版本的智能匹配规则的关键代码如下,翻译成人话就是:
- 如果JavaBean字段有
@JSONField
注解且name不空时,则取name的值 - 否则取JavaBean的字段名,忽略字母大小写和
-
,_
两个字符 - JSON中的key忽略
is
开头并忽略剩余字母大小写和-
,_
两个字符
if (this.smartMatchHashArray == null) {
long[] hashArray = new long[sortedFieldDeserializers.length];
for (int i = 0; i < sortedFieldDeserializers.length; i++) {
// 1. @JSONField的name不空时取该值直接与JSON中的key做匹配
// 2. 取JavaBean字段名忽略字母大小写和'-','_'两个字符尝试与JSON中的key做智能匹配
hashArray[i] = sortedFieldDeserializers[i].fieldInfo.nameHashCode;
}
Arrays.sort(hashArray);
this.smartMatchHashArray = hashArray;
}
// 对JSON中没有成功映射JavaBean的key做智能匹配
// 1. 直接匹配
long smartKeyHash = TypeUtils.fnv1a_64_extract(key);
int pos = Arrays.binarySearch(smartMatchHashArray, smartKeyHash);
// 2. 忽略key的字母大小写和'-','_'两个字符
if (pos < 0) {
long smartKeyHash1 = TypeUtils.fnv1a_64_lower(key);
pos = Arrays.binarySearch(smartMatchHashArray, smartKeyHash1);
}
// 3. 如果key以'is'开头,则忽略'is'开头并忽略剩余字母大小写和'-','_'两个字符
boolean is = false;
if (pos < 0 && (is = key.startsWith("is"))) {
smartKeyHash = TypeUtils.fnv1a_64_lower(key.substring(2));
pos = Arrays.binarySearch(smartMatchHashArray, smartKeyHash);
}
// 优先取@JSONField的name字段直接与JSON中的key做匹配
// 其次取JavaBean字段名忽略字母大小写和'-','_'两个字符尝试与JSON中的key做智能匹配
private long nameHashCode64(String name, JSONField annotation)
{
if (annotation != null && annotation.name().length() != 0) {
return TypeUtils.fnv1a_64_extract(name);
}
return TypeUtils.fnv1a_64_lower(name);
}
高版本与低版本的智能匹配规则差异就是:如果JavaBean字段有@JSONField
注解且name不空时,低配版对name的值会忽略字母大小写和-
,_
两个字符,而高版本则直接取name的值不会做忽略操作。
因此示例中加了@JSONField
注解的labelId
字段才会因为FastJSON版本不同而导致反序列化结果的不同。
在对FastJSON的最新几个版本挨个排查后定位出智能匹配规则发生修改的版本为1.2.71
,所以如果代码中使用了智能匹配,那么建议谨慎升级到1.2.71
及其更高的版本。
另外这么明显的未向前兼容的规则修改,应该有很多人会踩坑。于是去FastJSON的GitHub上查看后果然已经有人提出了issues:1.2.71以上版本加了JSONField的字段无法反序列化。
Recommend
-
205
fastjson Fastjson is a Java library that can be used to convert Java Objects into their JSON representation. It can also be used to convert a JSON string to an equivalent Java object. Fastjson can work with arbitrary Java objects includi...
-
194
README.md fastjson: optimized standard library JSON for Go fastjson has the same API as json from standard library encoding/json. The Unmarshal and ...
-
103
README.md
-
58
0x00 fastjson fastjson 是一个非常流行的库,可以将数据在JSON和Java Object之间互相转换,但是在2017年官方主动爆出了fastjson的反序列化漏洞以及
-
48
线上代码对日志的记录,重要性自不必说。但是怎样记录日志也是有讲究的! 日志可以直接在每个方法中进行日志记录,优点是想怎么记就怎么记,缺点是记日志的代码可能会超过你的业务代码,可读性急剧下降,这也是日志框架蓬勃...
-
40
未央 - 智能匹配,匿名 3 分钟陌生人社交 - NEXT
-
6
欧雷说:「我觉得单纯的文本匹配不算“智能”,至少得语义匹配才行。」 欧雷 发表于 2021-09-22 16:56 我觉得单纯的文本匹配不算「智能」,至少得语义匹配才行。
-
9
FastJson 反序列化部分字段为 Null 2020.7.20...
-
9
智能匹配无需投简历,澳大利亚招聘平台Getahead获138万美元融资 作者:阿宅 发布时间: 2023-02-20 18:33
-
7
0x01业务描述 说明: 同事搭建的业务系统,最开始使用 log4net 记录到本地日志. 然后多个项目为了日志统一,全部记录在 Elasticsearch ,使用 log4net.ElasticSearchAppender.DotNetCore. 然后搭建了 Kiban...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK