在開發業務中有一個請求接受實體大緻如下:
@Data
public class ActivityAddQuery {
/**
* a類标簽
*/
@NotBlank(message="a類标簽不能為空")
@Length(max=64,message = "")
private String aLabel;
}
在前端請求參數設定為{“aLabel”:“aaa”}時後端接受到的參數一直為空,查詢資料發現,spring的jackson參數發現順序,依次如下:
1.所有被public修飾的字段;
2.所有被public修飾的getter;
3.所有被public修飾的setter。
由于aLabel被設定成private,不符合第1條,到了第二條得到的是getALabel()方法,jackson不知道第一個A是大寫還是小寫,預設将從第一個大寫開始的所有大寫字母識别成小寫,即alabel,讀取setALabel()也一樣。例如屬性aLLbel也會由getALLabel()識别成allbel。
解決方案: 添加@JsonProperty(“aLabel”)指定json名稱即可。
@Data
public class ActivityAddQuery {
/**
* a類标簽
*/
@JsonProperty("aLabel")
private String aLabel;
}
想要知道原因,首先檢視編譯之後的的class為
@Data
public class ActivityAddQuery {
/**
* a類标簽
*/
@JsonProperty("aLabel")
private String aLabel;
public String getALabel() {
return this.aLabel;
}
@JsonProperty("aLabel")
public void setALabel(String aLabel) {
this.aLabel = aLabel;
}
}
稍微debug一下jackson,截取一部分如下:
protected void collectAll()
{
LinkedHashMap<String, POJOPropertyBuilder> props = new LinkedHashMap<String, POJOPropertyBuilder>();
// First: gather basic data
_addFields(props);
_addMethods(props);
// 25-Jan-2016, tatu: Avoid introspecting (constructor-)creators for non-static
// inner classes, see [databind#1502]
if (!_classDef.isNonStaticInnerClass()) {
_addCreators(props);
}
_addInjectables(props);
// Remove ignored properties, first; this MUST precede annotation merging
// since logic relies on knowing exactly which accessor has which annotation
_removeUnwantedProperties(props);
// and then remove unneeded accessors (wrt read-only, read-write)
_removeUnwantedAccessor(props);
// Rename remaining properties
_renameProperties(props);
// then merge annotations, to simplify further processing
// 26-Sep-2017, tatu: Before 2.9.2 was done earlier but that prevented some of
// annotations from getting properly merged
for (POJOPropertyBuilder property : props.values()) {
property.mergeAnnotations(_forSerialization);
}
// And use custom naming strategy, if applicable...
PropertyNamingStrategy naming = _findNamingStrategy();
if (naming != null) {
_renameUsing(props, naming);
}
/* Sort by visibility (explicit over implicit); drop all but first
* of member type (getter, setter etc) if there is visibility
* difference
*/
for (POJOPropertyBuilder property : props.values()) {
property.trimByVisibility();
}
/* and, if required, apply wrapper name: note, MUST be done after
* annotations are merged.
*/
if (_config.isEnabled(MapperFeature.USE_WRAPPER_NAME_AS_PROPERTY_NAME)) {
_renameWithWrappers(props);
}
// well, almost last: there's still ordering...
_sortProperties(props);
_properties = props;
_collected = true;
}
序列化的步驟大緻如下:
-
首先是讀取屬性----- _addFields(props);
将是以可能需要序列化的屬性加入到未過濾的屬性集中,即将屬性aLabel加入到 props 中。(在之後的過濾中,一般會過濾掉非public修飾的屬性,但如果有@JsonProperty注解,一樣會保留)。
-
然後再讀取get、set和@JsonAnySetter注解的方法------ _addMethods(props);
将是以可能需要序列化的由get、set和@JsonAnySetter注解的方法中擷取的屬性加入到未過濾的屬性集中。由于讀取規則,将getALbel()和setALbel()讀取成alabel,于是将屬性alabel加入到 props 中。
- 之後就是一系列的過濾和名稱修正,此時props中其實有alabel和aLabel兩個屬性,但是在之後的屬性過濾中,由getALbel和setALbel讀取來的alabel屬性,在getALabel上有 @JsonProperty(“aLabel”)明确的命名(nameExplicit)為aLabel,在最後才會得到正确的唯一的json屬性aLabel。
有時候序列化之後多出來一個屬性,這裡也就可以解釋的通了。例如下:
@Data
public class ActivityAddQuery {
/**
* 或者去掉 @JsonProperty("aLabel")注解,将private 改為 public
*/
@JsonProperty("aLabel")
private String aLabel;
}
public String getALabel() {
return this.aLabel;
}
public void setALabel(String aLabel) {
this.aLabel = aLabel;
}
由于讀取類屬性是擷取到了aLabel,讀取get和set方法時讀取到了alabel,由于沒有上面的對從get和set方法擷取來的屬性alabel強制命名為aLabel,是以序列化出來是這樣的:
是以需要像lombok在get或set上加@JsonProperty強制命名。
其實在idea中右鍵生成get set方法時,idea會智能的将類似aLabel的可能有歧義的getset方法設定成getaLabel()和setaLabel(),并不會将首字母A大寫,也就不會有序列化時的讀取歧義。
public class Test {
private String aLabel;
private String abc;
public String getaLabel() {
return aLabel;
}
public void setaLabel(String aLabel) {
this.aLabel = aLabel;
}
public String getAbc() {
return abc;
}
public void setAbc(String abc) {
this.abc = abc;
}
}