fastjson轉換json時,碰到的那些首字母大小寫轉換的坑!

某年某月的某一天,本汪在某個奇葩的公司,接手了某個奇葩的項目,遇到了一些奇葩的事情,就掉進關於fastjson作bean to json轉換時,那些關於首字符大小寫的坑。java

這個奇葩項目裏面,api接口定義的是天馬行空、雲山霧繞,api裏面的字段定義更是五花八門、千奇百怪,徹底沒有規則可言,均可以開個不符合規範的案例博物館了。下面3個坑裏面舉的例子,那些奇葩的名稱定義,都是在項目裏面真實存在的,若有雷同,純屬不幸。json

在api的處理過程裏面,bean轉換成json時,咱們老是但願字段名是什麼樣的,轉換成json就應該是什麼樣的,然而現實老是殘酷的。api

坑0x01 全大寫的鍵名

這種狀況在api定義裏面很常見,以下:eclipse

private String TEST = 「1」;
public String getTEST() { return this.TEST}

在fastjson轉換後,變成:json{ 「tEST」: 」1」},與預期不符,首字母變成小寫了。iphone

緣由:
fastjson在將bean轉換爲json時,先取出對應的methodName: getTEST,從methodName的第4個字符開始,取出propertyName:TEST,它默認認爲這個propertyName的首字母是在定義getter的時候被寫成大寫的,如今要轉成小寫:
propertyName = Character.toLowerCase(methodName.charAt(3)) + methodName.substring(4);
結果,正確的TEST就被轉成錯誤的tEST了。ide

解決:
解決這個問題比較簡單,在轉換前,多加一行代碼:
TypeUtils.compatibleWithJavaBean =true;
此時,fastjson會先判斷propertyName長度大於一、且頭兩個字符都是大寫時,不作轉換:this

if(name.length() > 1 && Character.isUpperCase(name.charAt(1)) && Character.isUpperCase(name.charAt(0))){
    return name;
}

也就是說,連續大寫開頭的propertyName,在轉換時,會保持原樣。idea

坑0x02 第一個字符大寫,第二個字符不是大寫的鍵名

按坑0x01裏面講的設置TypeUtils.compatibleWithJavaBean=true後,若是有以下的鍵名,依然會出問題:code

private String T_EST = 「1」;
private String Test = 「2」;
public String getT_EST() { return this.T_EST}
public String getTest() { return this.Test}

在fastjson轉換後,變成:{ 「t_EST」: 」1」, 「test」:」2」},又是一個坑。接口

緣由:
從坑0x01的最後一段代碼能夠看出,要想fastjson不改首字符,除了須要設置compatibleWithJavaBean外,propertyName的第二個字符也必須是大寫。在坑0x02這裏,兩個propertyName都沒法經過判斷,首字母就要被轉換成小寫了。

解決
要解決這個問題,在轉換前,必須再加一行代碼:
TypeUtils.compatibleWithFieldName = true;
Fastjson裏面對應的處理代碼以下:

if(compatibleWithFieldName){
    if(!fieldCacheMap.containsKey(propertyName)){
        String tempPropertyName = methodName.substring(fromIdx);
        return fieldCacheMap.containsKey(tempPropertyName) ? tempPropertyName : propertyName;
    }
}

fastjson在按坑0x01所述的過程處理完propertyName後,propertyName的首字符被轉換爲小寫,而後會在bean的的field列表裏面找一遍轉換後的名稱。若是找不到,就從methodName裏面從新取get後面的字符串,而後再到field列表裏面找一遍,若是找到,就用原propertyName,若是找不到就用首字符被轉換的名稱。

坑0x03 首字符小寫,第二個字符大寫的鍵名

這個坑與lombok相關,嚴格來講,應該是lombok挖的坑。
如上所述,就算你按坑0x0一、0x02設置了,若是在工程裏面用lombok時,有以下的鍵名定義,依然要被坑:

@Getter
private String iPhone = 「1」;

在fastjson轉換後,變成:{ 「IPhone」: 」1」 },首字符變大寫了,WTF!

緣由:
Lombok在自動生成getter的時候,會把propertyName的第一的字母改爲大寫,等同以下代碼:

public String getIPhone(){return this.iPhone;}

問題就出在這個轉換上,get後面緊跟的字符變成大寫了。在eclipse、idea裏面,若是自動生成代碼,get後的字符是小寫。

如前面坑0x01最後一段代碼,fastjson在處理getter時,會判斷前兩個字符是否是大寫,若是是的話,就保持原樣,取到的propertyName就成了:IPhone。可是在field列表裏面,對應的propertyName是iPhone,就算開啓了compatibleWithFieldName,fastjson用從getter中解析出來的IPhone,在field列表裏面也找不到對應值,也只能保持IPhone這個名稱了。

另外,就算在屬性前用了@JSONField(name = 「iPhone」)註解,由於fastjson用從getter解析出來的propertyName找不到對應的field,也沒法讀出該field對應的註解,這個註解也是無效的。

解決

  1. 碰到這種propertyName,就不要用lombok生成的getter/setter了,本身寫,保持首字符小寫,如:getiPhone/setiPhone,只要在代碼裏面有這兩個方法,lombok就不會自動生成,lombok是按忽略大小寫後的propertyName判斷的。
  2. 把propertyName第二個字符改爲小寫,或者從新取個名字,並用@JSONField註明正確的名稱,如:

    @Getter
    @ JSONField(name = 「iPhone」)
    private String iphone = 「1」;

    關於lombok的這個坑,能夠參考:http://xxxx.ooo/2017/using-lo...,lombok的做者也是死鴨子嘴硬,被提了n多的issue,就是不改。

以上,就是本汪在項目裏面填坑的過程,不要問我爲何api裏面的字段名稱會定義成這副模樣,本汪也不知道,汪!汪!!汪!!!

關於fastjson的代碼,都在TypeUtils.computeGetters裏面。@JSONField是個好東西,不嫌麻煩的話,就都加上吧。

相關文章
相關標籤/搜索