記錄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生成的方法。