天天看點

傳參請求時jackson大小寫取值異常和序列化多出一個屬性的問題學習記錄

在開發業務中有一個請求接受實體大緻如下:

@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;
    }
}