Java大联盟
帮助万千Java学习者持续成长
关注
项目和第三方对接,有接口一直调不通,直到我拿到合作方的入参后,问题来了,有了接下来的一系列故事。
拿到原生的第三方参数( JSON 格式的),为了节省时间,迫不及待地直接在本地单元测试一波。熟悉的操作,这里为了方便观察,我模拟一次请求从 Controller 进入的请求:
import com.alibaba.fastjson.JSON;import lombok.Data;
public class ProFastjson {
@Data
static class Teacher {
private String teacherName;
}
private static TeacherController teacherController = new TeacherController();
public static void main(String[] args) {
String str = "{\"teacher_name\":\"zxerjones\"}";
Teacher teacher = JSON.parseObject(str, Teacher.class);
teacherController.search(teacher);
}
}
看重点:我的 JSONString 参数名有下划线,Teacher 的是没有 teacher_name 这个属性的,反序列化之后的值应该是空的,可偏偏方法走通了。不甘心的我决定 debug 一波,如下图:
反序列化结果
反序列化成功了,是不是一脸懵逼。没错 FASTJson 就是不和你讲道理。行吧,那一定是是 FASTJson 内部的反序列化机制造成这样的结果,debug 进源码看看:
JSON.parseObject 方法内部
注意看上图 378 行,JSON 的反序列化就是在这个方法中实现的,继续跟进:
DefaultJSONParser.parseObject 方法内部
上图对一些规则进行校验之后,开始进入主题。正式调用反序列化工具 JavaBeanDeserializer.deserialze 对字符进行反序列化。这个方法主要做的事情就是对 JSON 字符串内部的键值对和我们需要的反序列化结果类进行绑定,赋值(这个方法代码比较多,只贴重点):下图是赋值的过程,这才是导致问题的关键:
JavaBeanDeserializer.deserialze 方法内部
就是 850 行代码,终于找到问题的所在了,马上就真相大白。现在可以确定就是 parseField 方法搞的鬼,继续点进去:
parseField 方法内部
上图方法内部做的事情就是生成 field 反序列化工具对 field 反序列化,然后赋值。这个 field 反序列化工具从何而来断点打到 1089 行,可以看到,这个方法返回了我们需要的工具,点进去:
JavaBeanDeserializer.smartMatch 方法内部
代码做的事情就是根据 JSON 字符串的键(teacher_name)做一次 hash 运算,然后与需要反序列化结果(Teacher.class)中的所有参数(这里只有teacherName)做 hash 运算,根据生成的 hash 值来对 field 进行配对。OK就是这样的,现在我们已经找到问题的根源,hash 之后的值是相同的。fastjosn 内部封装了计算 hash 的工具类,TypeUtils.fnv1a_64_lower,如下图:
TypeUtils.fnv1a_64_lower
代码中可以看到进行hash的两个规则:
1、‘-’和‘_’不参与hash运算。
2、忽略大小写。
根据最后得出的 hash 规则,那么可以在 json 字符串中随意的添加"_","-",并且大小写忽略都可以反序列化成功,证实下猜想:如下代码。
public static void main(String[] args) { String str = "{\"TEACHER___---NAME\":\"zxerjones\"}";
Teacher teacher = JSON.parseObject(str, Teacher.class);
System.out.println(teacher);
}
运行结果:
ProFastjson.Teacher(teacherName=zxerjones)
Process finished with exit code 0
猜想成立,问题解决。血一样的教训:能用 postman 就不要用单元测试。~~(╯﹏╰)~~
推荐阅读
1、一次性把JVM讲清楚,别再被面试官问住了
2、Spring Boot源码解析
3、一文搞懂前后端分离
4、快速上手Spring Boot+Vue前后端分离
楠哥简介
资深 Java 工程师,微信号 nnsouthwind
《Java零基础实战》一书作者,今日头条认证大V
GitChat认证作者,B站认证UP主(楠哥教你学Java)
致力于帮助万千 Java 学习者持续成长。