某年某月的某一天,本汪在某個奇葩的公司,接手了某個奇葩的項目,遇到了一些奇葩的事情,就掉進關於fastjson作bean to json轉換時,那些關於首字符大小寫的坑。java
這個奇葩項目裏面,api接口定義的是天馬行空、雲山霧繞,api裏面的字段定義更是五花八門、千奇百怪,徹底沒有規則可言,均可以開個不符合規範的案例博物館了。下面3個坑裏面舉的例子,那些奇葩的名稱定義,都是在項目裏面真實存在的,若有雷同,純屬不幸。json
在api的處理過程裏面,bean轉換成json時,咱們老是但願字段名是什麼樣的,轉換成json就應該是什麼樣的,然而現實老是殘酷的。api
這種狀況在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
按坑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,若是找不到就用首字符被轉換的名稱。
這個坑與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對應的註解,這個註解也是無效的。
解決
把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是個好東西,不嫌麻煩的話,就都加上吧。