记一次接口调用字段映射失败问题排查

在写接口的时候遇到一个很神奇的问题,编写一个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反序列化失败的更多相关文章

  1. jackson json序列化 首字母大写 第二个字母需小写

    有这样一个类: @Setter @Getter @JsonNaming(value = PropertyNamingStrategy.UpperCamelCaseStrategy.class) pub ...

  2. fastjson转换json字符串key的首字母小写变大写的解决办法

    https://blog.csdn.net/erbao_2014/article/details/53688934 问题描述在开发过程中,由于接口文档的描述,要求json字符串的key首字母为大写,而 ...

  3. 一起写框架-Ioc内核容器的实现-基础功能-容器对象名默认首字母小写(八)

    实现功能 --前面实现的代码-- 默认的对象名就类名.不符合Java的命名规范.我们希望默认的对象名首字母小写. 实现思路 创建一个命名规则的帮助类.实现将对大写开头的对象名修改为小写开头. 实现步骤 ...

  4. C#实体对象序列化成Json并让字段的首字母小写的两种解决方法

    引言:最近在工作中遇到与某些API对接的post的数据需要将对象的字段首字母小写.解决办法有两种:第一种:使用对象的字段属性设置JsonProperty来实现(不推荐,因为需要手动的修改每个字段的属性 ...

  5. C#实体对象序列化成Json,并让字段的首字母小写

    引言:最近在工作中遇到与某些API对接的post的数据需要将对象的字段首字母小写.解决办法有两种:第一种:使用对象的字段属性设置JsonProperty来实现(不推荐,因为需要手动的修改每个字段的属性 ...

  6. c# MVC返回小驼峰Json(首字母小写)

    1.与前端交互时,前端总希望传过去的json字段名首字母小写,但是.net规范是首字线大写 如果就写了下面的转换方法 /// <summary> /// Poco类字段名转换成首字母小写的 ...

  7. 关于spring的注解方式注入默认值(转) -- 首字母小写

    1.是首字母小写 比如 UserAction对应的id是userAction 可以通过ApplicationContext 对象的act.getBean("userAction") ...

  8. IntelliJ IDEA的自动提示貌似是区分大小写的,首字母小写的话,怎么都提示不出来。

    IntelliJ IDEA的自动提示貌似是区分大小写的,首字母小写的话,怎么都提示不出来. File>Settings>editor >general >code comple ...

  9. webapi时间字段返回格式设置及返回model首字母小写

    GlobalConfiguration.Configuration.Formatters.Remove(new XmlMediaTypeFormatter()); // 解决json序列化时的循环引用 ...

  10. 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.特 ...

随机推荐

  1. aos.js 与 swiper 组合,翻页后无法触发aos的效果

    手动给除第一页之外的需要特效的元素添加 class="aos-animate" 转自:https://cloud.tencent.com/developer/ask/sof/302 ...

  2. can't convert CUDA tensor to numpy. Use Tensor.cpu() to copy the tensor to host memory first.

    predict=predict.data.numpy() 这一行报错意思是:如果想把CUDA tensor格式的数据改成numpy时,需要先将其转换成cpu float-tensor随后再转到nump ...

  3. python题mhy

    def save(s): f=open("test.txt","wt") f.write(s["name"]+"\n") ...

  4. airtest的手势滑动方法封装

    ​ 这个网上应该很多类似的方法封装,各种实现方式也很多,但是感觉最简单实用的还是swipe了:代码很简单,直接上方法了. 很多方法都不会告诉你会导入什么包,其实很多小白入门可能就是这么简单的一步就被卡 ...

  5. 关于 'vue-cli-service' 不是内部或外部命令,也不是可运行的程序 或批处理文件 的处理

    关于 npm run serve 之后 'vue-cli-service' 不是内部或外部命令,也不是可运行的程序 或批处理文件 一.安装node.js 去官网安装Node.js(地址:https:/ ...

  6. java15配置环境后java_version无反应(不显示“不是内部或外部命令”)

    重新装了jdk15来使用eclipse 配置完环境变量之后打开cmd输入 java -version 好家伙,居然一点反映都没有, 然后傻乎乎的跑回去重新配置JAVA_HOME和path 还是没用,细 ...

  7. 第四章 快速排序 分而治之(divide an conquer)

    def quicksort(array): if len(array) < 2: return array else: flag = array[0] less = [] greater = [ ...

  8. spacy词向量

    spaCy能够比较两个对象,并预测它们的相似程度. 预测相似性对于构建推荐系统或标记重复项很有用. 例如,您可以建议与当前正在查看的用户内容相似的用户内容,或者将支持凭单标记为与现有内容非常相似的副本 ...

  9. 各种系统名词解释:MIS 、ERP、CRM、OA

    MIS :信息系统.针对企业使用的软件,都可以叫做MIS系统. (管理信息系统--Management Information System)系统 ,是一个由人.计算机及其他外围设备等组成的能进行信息 ...

  10. python调用java&反编译地址

    反编译工具地址: https://github.com/java-decompiler/jd-gui/releases 你想知道的JPype全在这里∞   先总结自己趟的坑 1. python进程是6 ...