摘要
今天踩了Jackson和Lombok的坑,反序列化json时缺少字段名,原来是因为Jackson和Lombok的setter不一致,导致无法正确启用。我在Java dao层使用@Data注解定义了一个DemoData类,它包含了一个字符串数组。
正文
纪录Jackson和Lombok的坑
今日碰到Jackson反序列化json缺乏了字段名,之后科学研究下发觉是Jackson的体制和Lombok转化成的setter不一致,造成沒有恰当启用setter。
重现
Javadao层
@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那样的(单独英文字母跟随英文大写字母)的特性时,反序列化会遗失这一字段名。
如何解决
DeLombok
当编码中发生那样的字段名时,由IDEA转化成相匹配的getter和setter,会全自动遮盖lombok转化成的方式。
应用Builder来做Jackson的反序列化器
Lombok好像观念到这个问题(因此为什么不变下setter的转化成呢???),撰写了@Jacksonized
这一注释来为Jackson反编码序列给予适用,可是这一注释务必相互配合@Builder
或是@SuperBuilder
一起应用才会起效。
大家看下@Jacksonized
的官方网表明:
/**
* The {@code @Jacksonized} annotation is an add-on annotation for
* {@code @}{@link Builder} and {@code @}{@link SuperBuilder}. It automatically
* configures the generated builder class to be used by Jackson's
* deserialization. It only has an effect if present at a context where there is
* also a {@code @Builder} or a {@code @SuperBuilder}; a warning is emitted
* otherwise.
* <p>
* In particular, the annotation does the following:
* <ul>
* <li>Configure Jackson to use the builder for deserialization using
* {@code @JsonDeserialize(builder=Foobar.FoobarBuilder[Impl].class)}
* on the class (where <em>Foobar</em> is the name of the annotated class).</li>
* <li>Copy Jackson-related configuration annotations (like
* {@code @JsonIgnoreProperties}) from the class to the builder class. This is
* necessary so that Jackson recognizes them when using the builder.</li>
* <li>Insert {@code @JsonPOJOBuilder(withPrefix="")} on the generated builder
* class to override Jackson's default prefix "with". If you configured a
* different prefix in lombok using {@code setterPrefix}, this value is used. If
* you changed the name of the {@code build()} method using using
* {@code buildMethodName}, this is also made known to Jackson.</li>
* <li>For {@code @SuperBuilder}, make the builder implementation class
* package-private.</li>
* </ul>
* This annotation does <em>not</em> change the behavior of the generated builder.
* A {@code @Jacksonized} {@code @SuperBuilder} remains fully compatible to
* regular {@code @SuperBuilder}s.
*/
简易而言,这一注释会做下边的事:
- 会根据
@JsonDeserialize
注释让Jackson应用Builder来搭建目标; - 复制Jackson有关的注释到Builder中(例如
@JsonIgnoreProperties
); - 转化成的Builder类会加上
@JsonPOJOBuilder
注释并载入prefix;
因而,把上边的Pojo改变成那样:
@Data
@Builder
@Jacksonized
public class DemoData {
private Double t;
private Double eDay;
}
会转化成下边的POJO:
@JsonDeserialize(
builder = DemoData.DemoDataBuilder.class
)
public class DemoData {
private Double t;
private Double eDay;
DemoData(final Double t, final Double eDay) {
this.t = t;
this.eDay = eDay;
}
public static DemoData.DemoDataBuilder builder() {
return new DemoData.DemoDataBuilder();
}
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;
}
public boolean equals(final Object o) {
if (o == this) {
return true;
} else if (!(o instanceof DemoData)) {
return false;
} else {
DemoData other = (DemoData)o;
if (!other.canEqual(this)) {
return false;
} else {
Object this$t = this.getT();
Object other$t = other.getT();
if (this$t == null) {
if (other$t != null) {
return false;
}
} else if (!this$t.equals(other$t)) {
return false;
}
Object this$eDay = this.getEDay();
Object other$eDay = other.getEDay();
if (this$eDay == null) {
if (other$eDay != null) {
return false;
}
} else if (!this$eDay.equals(other$eDay)) {
return false;
}
return true;
}
}
}
protected boolean canEqual(final Object other) {
return other instanceof DemoData;
}
public int hashCode() {
int PRIME = true;
int result = 1;
Object $t = this.getT();
int result = result * 59 ($t == null ? 43 : $t.hashCode());
Object $eDay = this.getEDay();
result = result * 59 ($eDay == null ? 43 : $eDay.hashCode());
return result;
}
public String toString() {
Double var10000 = this.getT();
return "DemoData(t=" var10000 ", eDay=" this.getEDay() ")";
}
@JsonPOJOBuilder(
withPrefix = "",
buildMethodName = "build"
)
public static class DemoDataBuilder {
private Double t;
private Double eDay;
DemoDataBuilder() {
}
public DemoData.DemoDataBuilder t(final Double t) {
this.t = t;
return this;
}
public DemoData.DemoDataBuilder eDay(final Double eDay) {
this.eDay = eDay;
return this;
}
public DemoData build() {
return new DemoData(this.t, this.eDay);
}
public String toString() {
return "DemoData.DemoDataBuilder(t=" this.t ", eDay=" this.eDay ")";
}
}
}
这时,Jackson会应用建造者方式来搭建目标,载入特性,Json还可以一切正常分析了。
关注不迷路
扫码下方二维码,关注宇凡盒子公众号,免费获取最新技术内幕!
评论0