记录Jackson和Lombok的坑
记录Jackson和Lombok的坑
今天遇到Jackson反序列化json缺少了字段,后来研究下发现是Jackson的机制和Lombok生成的setter不一致,导致没有正确调用setter。
复现
Java实体类
@Data
public class DemoData{
private Double t;
private Double eDay;
}
Json字符串
{
"t":12.23,
"eDay":123.321
}
使用Jackson解析下来,发现只有t有值,而eDay没有解析到。
原因分析
首先第一反应是Lombok生成的getter和setter也许有问题,于是去掉@Data
注解,用IDEA生成getter和setter,再进行反序列化,发现已经可以正常反序列化了。
于是看了下编译生成的代码:
public class DemoData{
private Double t;
private Double eDay;
public Double getT() {
return this.t;
}
public Double getEDay() {
return this.eDay;
}
public void setT(final Double t) {
this.t = t;
}
public void setEDay(final Double eDay) {
this.eDay = eDay;
}
}
去掉lombok的注解,直接用IDEA生成getter和setter,生成之后是这样的:
public class DemoData{
private Double t;
private Double eDay;
public Double getT() {
return t;
}
public void setT(Double t) {
this.t = t;
}
public Double geteDay() {
return eDay;
}
public void seteDay(Double eDay) {
this.eDay = eDay;
}
}
显然两边的Getter和Setter是不一样的,那么Jackson是怎么寻找属性和Setter的呢?
Jackson2在初始化序列器时,对pojo类型对象会收集其属性信息,属性包括成员变量及方法,然后属性名称和处理过后的方法名称做为key保存到一个LinkedHashMap中。
收集过程中会调用com.fasterxml.jackson.databind.util.BeanUtil中的legacyManglePropertyName方法用来处理方法名称,它会将get/set方法前缀,即get或set去掉,并将其后面的连续大写字符转换成小写字符返回。
例如: getNEWString会转变成newstring返回。你的属性名称如果有这样的"nSmallSellCount",lombok自动生成的get方法就会是这样的"getNSmallSellCount",处理过后就是这样的"nsmallSellCount",这与属性nSmallSellCount并不冲突,可以同时存在于HashMap中。
所以,当Jackson扫描由Lombok生成的POJO时,读取到setEDay,会把set去掉,拿到EDay,然后转成eday。由此导致json中的eDay属性在LinkedHashMap中没有找到setter方法,反序列化就丢失了字段。
所以原因已经确定了:当使用Lombok修饰的POJO中存在由aAxxx这样的(单个小写字母跟着大写字母)的属性时,反序列化会丢失这个字段。
如何解决
@JsonProperty
对出问题的字段添加@JsonProperty
注解,并设置字段名,就可以正确解析了。
DeLombok
当代码中出现这样的字段时,由IDEA生成对应的getter和setter,会自动覆盖lombok生成的方法。