Lombok首字母小写,第二个字母大写,jackson反序列化失败
记一次接口调用字段映射失败问题排查
在写接口的时候遇到一个很神奇的问题,编写一个post接口,在使用包装类接收body的时候发现有个字段映射不上。代码如下
@RestController
public class TestController {
@PostMapping("test")
public TestDto test(@RequestBody TestDto testDto) {
return testDto;
}
}
..........
@Getter
@Setter
public class TestDto {
private String sName;
private String value;
}
TestDto中的value可以正常获取值,但是sName却没值。
根据我多年的开发经验,推测应该是字段名的问题,大家都知道springboot接口反序列化是用的jackson,而jackson又是调用的getter和setter实现的序列化和反序列化,这样咱们大概就有一个方向。直接看一下class文件,看看@getter和@setter注解生成的方法长什么样

看起来感觉没什么问题。
那就只能debug看了,为了方便调试,手动编写getter和setter方法(复制过来),然后在setSName方法上打上断点,结果根本没停,说明根本就没有调用该方法。那我们再试试在setValue方法上打上断点,这次走到了

看一下方法调用,发现前几个都是invoke,没啥用,往前看几个,发现deserializeAndSet方法,没看到什么有用的信息

再往前看一个,看看deserializeFromObject方法里面,发现这么一个判断

看一下_beanProperties

发现这里就是保存类字段的信息地方,但是,嗯?为什么是sname,难怪我们的sName无法映射上。
那我们接下来就找找看,这个_beanProperties到底是怎么来的

在_beanProperties上打上断点,调一下接口,嗯?怎么没停?难不成这个是系统启动的时候就加载好的,重启一下试试。还是没有!再调一下接口试试,这次终于是停了

我们再来看看这properties是哪来的

继续深入

终于看到了我们熟悉的内容,这不就是我们要找的东西吗,发现又是从props来的,再回头

继续在_properties上打上断点,注意一定要把Field access勾选上,不然没办法监听到参数的使用

重启服务,调用接口,然后进入addProperty方法

然后继续反推,发现propDefs

阅读方法后发现数据来源于beanDesc.findProperties(),深入得到

继续在这个新的_properties上打上断点,重启服务,跳过几个无用断点,调试接口

深入方法,发现collectAll方法

看到_addFields和_addMethods方法,感觉终于要接近真相了!
接下来我们就看看collectAll方法到底做了什么

可以看到,在_addFields之后,props里面已经存放了TestDto的两个字段,这个时候sName的key还是对的

进入_addMethods方法,这段代码的逻辑是遍历类里的所有方法,如果方法的入参是0个就尝试从add getter方法,如果方法入参是1个则尝试add setter方法。
进入_addGetterMethod方法,发现如下逻辑,如果方法上没有添加json注解就会尝试从方法名称中解析字段名

进入findNameForRegularGetter方法,发现最终是调用的legacyManglePropertyName解析字段名

进入该方法,终于真相大白,重点看下面这段逻辑

这个方法中和方法名一起传进来的还有getter字符的offset用于去除get前缀,然后得到方法中的属性名,首先将属性名的第一个字符变为小写,如果本来就是小写的话直接返回属性名,如getvalue->value。
如果不是则继续遍历剩余字符,将每一个遇到的大写字符都转化为小写,直到遇到第一个小写的字符,然后返回属性名。如getVAlue->value,
getSName->sname,getURL->url。本来_addMethods方法是为了给每个field添加对应的getter和setter方法的,但是现在由于从getter方法中解析的名称和真实的field不一致,就导致会新增一个该名称的字段,我们的sname就是这么来的


这个时候props里面实际上有三个字段,而sName里面是没有getter,setter方法的,我们的getSName方法被sname字段拿去了。然后在经过_removeUnWantedProperties(props)方法之后,没有getter和setter方法的字段就被移除了!

结论
jackson反序列化的逻辑是,先找到类的成员变量field,然后从getter setter方法中反推属性名,为field和方法添加映射,而jackson反推属性名的逻辑是方法中去掉get的部分后跟着的连续大写字符都转换为小写字符。
写在最后
实际上这也和lombok生成getter方法的逻辑有关,如果我用idea自动生成getter方法的话,这个逻辑和jackson是一致的

Lombok首字母小写,第二个字母大写,jackson反序列化失败的更多相关文章
- jackson json序列化 首字母大写 第二个字母需小写
有这样一个类: @Setter @Getter @JsonNaming(value = PropertyNamingStrategy.UpperCamelCaseStrategy.class) pub ...
- fastjson转换json字符串key的首字母小写变大写的解决办法
https://blog.csdn.net/erbao_2014/article/details/53688934 问题描述在开发过程中,由于接口文档的描述,要求json字符串的key首字母为大写,而 ...
- 一起写框架-Ioc内核容器的实现-基础功能-容器对象名默认首字母小写(八)
实现功能 --前面实现的代码-- 默认的对象名就类名.不符合Java的命名规范.我们希望默认的对象名首字母小写. 实现思路 创建一个命名规则的帮助类.实现将对大写开头的对象名修改为小写开头. 实现步骤 ...
- C#实体对象序列化成Json并让字段的首字母小写的两种解决方法
引言:最近在工作中遇到与某些API对接的post的数据需要将对象的字段首字母小写.解决办法有两种:第一种:使用对象的字段属性设置JsonProperty来实现(不推荐,因为需要手动的修改每个字段的属性 ...
- C#实体对象序列化成Json,并让字段的首字母小写
引言:最近在工作中遇到与某些API对接的post的数据需要将对象的字段首字母小写.解决办法有两种:第一种:使用对象的字段属性设置JsonProperty来实现(不推荐,因为需要手动的修改每个字段的属性 ...
- c# MVC返回小驼峰Json(首字母小写)
1.与前端交互时,前端总希望传过去的json字段名首字母小写,但是.net规范是首字线大写 如果就写了下面的转换方法 /// <summary> /// Poco类字段名转换成首字母小写的 ...
- 关于spring的注解方式注入默认值(转) -- 首字母小写
1.是首字母小写 比如 UserAction对应的id是userAction 可以通过ApplicationContext 对象的act.getBean("userAction") ...
- IntelliJ IDEA的自动提示貌似是区分大小写的,首字母小写的话,怎么都提示不出来。
IntelliJ IDEA的自动提示貌似是区分大小写的,首字母小写的话,怎么都提示不出来. File>Settings>editor >general >code comple ...
- webapi时间字段返回格式设置及返回model首字母小写
GlobalConfiguration.Configuration.Formatters.Remove(new XmlMediaTypeFormatter()); // 解决json序列化时的循环引用 ...
- js进阶正则表达式7点数字字母空格(w d s)(小写表原意,大写表反义)(特殊字符要加反斜杠:var reg22=/\W/g)
js进阶正则表达式7点数字字母空格(w d s)(小写表原意,大写表反义)(特殊字符要加反斜杠:var reg22=/\W/g) 一.总结 1.w d s,word digital space 2.特 ...
随机推荐
- 一个线程池的c++实现
前面我们实现了CallBack类,实现了对任意可调用对象的封装,且统一了调用接口. 现在利用CallBack类,我们来实现一个线程池,我们的线程池包含: 1. 状态机, 用于控制和管理线程池的运行.停 ...
- md5加密js内容
避免以后都得百度,直接留一份存档 /* * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message * Diges ...
- idea 部署项目到 docker 运行
1.在远程服务器上开启 docker 远程连接 $vim /usr/lib/systemd/system/docker.service # ExecStart=/usr/bin/dockerd -H ...
- uniapp的子组件,当父组件下来刷新时,子组件一同刷新。
最近做uniapp项目的时候,使用给父组件一个刷新属性,父组件有效果,但是子组件没有反应,网上查找了很多方法,最终采取通过刷新时,函数传值,子组件监听的方式.具体做法可以参照:https://blog ...
- opencv3 7.3 重映射 仿射变换
重映射的概念 将一幅图像某位置的像素放置到另外一幅图像的指定位置上,需要对非整数像素坐标重映射来表达每个像素的新位置. g(x,y)=f(h(x,y)); 实现重映射 remap()函数 dst(x, ...
- 如何修改被编译后DLL文件 (转发)
我们平时在工作中经常会遇到一些已经被编译后的DLL,而且更加麻烦是没有源代码可以进行修改,只能针对这个DLL的文件进行修改才能得到我们想要的结果:本文将通过一个实例来演示如果完成一个简单的修改;我们将 ...
- DTO的理解
首要的作用,我认为就是减少原生对象的多余参数.包括为了安全,有时候也为了节约流量.例如:密码,你就不能返回到前端.因为不安全. 其次假如说:获取博客列表的时候,也不能返回博客全文吧.顶多就返回标题,i ...
- 11.30linux学习第十一天
今天老刘上课,第7章收尾,第8章开了个头. 7.1.3 磁盘阵列+备份盘 RAID 10磁盘阵列中最多允许50%的硬盘设备发生故障,但是存在这样一种极端情况,即同一RAID 1磁盘阵列中的硬盘设备若 ...
- 树莓派3B+ wifi设置
环境: 硬件:树莓派 3b+ 固件:2018-04-18-raspbian-stretch.img 一.树莓派配置 1.1.wifi配置方法一(已测试,ok) 参考: https://www.cnbl ...
- mysqli语句的用法
改用了PHP7的环境后 mysql语句好像就不能用了.. 有点懵逼 就写一下该怎么用以免遗忘 ======= 1 链接数据库 $link=mysqli_connect('127.0.0.1:xxxx' ...