FastJSON字段智能匹配踩坑的解决

 更新时间:2021年6月18日 15:01  点击:2465

背景

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字段智能匹配失败的原因,首先要先了解智能匹配的规则。

低版本

低版本的智能匹配规则的关键代码如下,翻译成人话就是:

1、如果JavaBean字段有@JSONField注解且name不空时,则对name的值忽略字母大小写和-,_两个字符

2、否则取JavaBean的字段名,忽略字母大小写和-,_两个字符

3、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);
}

在这里插入图片描述

高版本

高版本的智能匹配规则的关键代码如下,翻译成人话就是:

1、如果JavaBean字段有@JSONField注解且name不空时,则取name的值

2、否则取JavaBean的字段名,忽略字母大小写和-,_两个字符

3、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的字段无法反序列化。

FastJSON解析数据,字段数据不匹配问题

FastJSON中@JSONField注解使用

有个联通的数据要解析出来存入数据库,但是提供过来的json数据有特殊符号'.','-',之前想着直接把特殊的字符给替换掉,解析出来

有一种是可以在实体类上加注解来替换转出来的

fastjson的key是根据javabean里面的getter和setter方法来的,不是根据属性名的,所以会出现这个问题,你在属性的get和set方法上面写上标注,说明转成什么就行了比如 @JSONField(name=”SOMETHING”)

之前想的是替换到json数据里面的特殊字符,然后把实体类的.-都替换掉,这样就可以创建实体类对象了,然后在用fastjson转成对象

后来知道有fastjson的注解的@JSONField(name="name.age-12"来映射上实体类的)

以上为个人经验,希望能给大家一个参考,也希望大家多多支持猪先飞。

[!--infotagslink--]

相关文章

  • 浅谈C# 字段和属性

    这篇文章主要介绍了C# 字段和属性的的相关资料,文中示例代码非常详细,供大家参考和学习,感兴趣的朋友可以了解下...2020-11-03
  • Mybatis Plus select 实现只查询部分字段

    这篇文章主要介绍了Mybatis Plus select 实现只查询部分字段的操作,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-09-01
  • mybatis-plus 返回部分字段的解决方式

    这篇文章主要介绍了mybatis-plus 返回部分字段的解决方式,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-10-02
  • PHP正则匹配img及标签各属性值(匹配图片函数)

    有一个项目要获取页面中所有img标签中的图片地址,这里我们使用到了preg_match_all正则函数,然后看我下面的一些参数即可实现了。 例 代码如下 复制代码 ...2016-11-25
  • postgresql数据添加两个字段联合唯一的操作

    这篇文章主要介绍了postgresql数据添加两个字段联合唯一的操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-02-04
  • PHP 二维数组根据某个字段排序的具体实现

    本文记录的要实现的功能类似于 MySQL 中的 ORDER BY,上个项目中有遇到这样的一个需求。 要求:从两个不同的表中获取各自的4条数据,然后整合(array_merge)成一个数组,再根据数据的创建时间降序排序取前4条。 遇到这个...2014-06-07
  • 一个关于JS正则匹配的踩坑记录

    这篇文章主要给大家介绍了一个关于JS正则匹配的踩坑记录,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-04-13
  • C# Dynamic关键字之:调用属性、方法、字段的实现方法

    本篇文章是对C#中调用属性、方法、字段的实现方法进行了详细的分析介绍,需要的朋友参考下...2020-06-25
  • Mybatis Plus 字段为空值时执行更新方法未更新解决方案

    这篇文章主要介绍了Mybatis Plus 字段为空值时执行更新方法未更新解决方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-09-03
  • js正则学习小记之匹配字符串字面量

    关于匹配字符串问题,有很多种类型,今天讨论 js 代码里的字符串匹配,因为我想学完之后写个语法高亮练手,所以用js代码当作例子...2021-05-07
  • postgresql 补齐空值、自定义查询字段并赋值操作

    这篇文章主要介绍了postgresql 补齐空值、自定义查询字段并赋值操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-01-16
  • js 正则学习小记之匹配字符串字面量优化篇

    昨天在《js 正则学习小记之匹配字符串字面量》谈到 /"(?:\\.|[^"])*"/ 是个不错的表达式,因为可以满足我们的要求,所以这个表达式可用,但不一定是最好的...2021-05-07
  • 解决Pytorch修改预训练模型时遇到key不匹配的情况

    这篇文章主要介绍了解决Pytorch修改预训练模型时遇到key不匹配的情况,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-06-05
  • OpenResty中正则模式匹配的2种方法详解

    在 OpenResty 中,同时存在两套正则表达式规范:Lua 语言的规范和 Nginx 的规范,下面这篇文章主要给大家介绍了关于OpenResty中正则模式匹配的2种方法,文中通过示例代码介绍的非常详细,需要的朋友可以参考下。...2020-06-30
  • python基于OpenCV模板匹配识别图片中的数字

    这篇文章主要介绍了python基于OpenCV模板匹配识别图片中的数字,帮助大家更好的理解和学习使用python,感兴趣的朋友可以了解下...2021-03-31
  • JS中正则表达式全局匹配模式 /g用法详解

    本文章通过实例代码给大家详细介绍js中正则表达式的全局匹配模式 g的用法,需要的朋友参考下...2017-04-03
  • 字符串替换Replace仅替换第一个字符串匹配项

    C#里面的String.Replace(string,string)方法替换的时候是替换所有的匹配项,我们需要只替换第一个匹配项,写一个方法来实现这个功能...2020-06-25
  • FastJSON字段智能匹配踩坑的解决

    这篇文章主要介绍了FastJSON字段智能匹配踩坑的解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-06-18
  • Swagger2匹配多个controller代码实例

    这篇文章主要介绍了Swagger2匹配多个controller代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下...2020-09-19
  • 详解Yii2定制表单输入字段的标签和样式的方法

    这篇文章详细介绍了Yii2定制表单输入字段的标签和样式的教程,有需要的同学可以参考一下,以备不时之需。 Yii2中对于表单和字段的支持组件为ActiveForm和ActiveField...2017-01-22