坑0x01 全大写的键名
这种情况在api定义里面很常见,如下:
private String TEST = "1";
public String getTEST() {
return this.TEST
}
在fastjson转换后,变成:json{ "tEST": "1"}
,与预期不符,首字母变成小写了。
原因:
fastjson在将bean转换为json时,先取出对应的methodName: getTEST,从methodName的第4个字符开始,取出propertyName:TEST,它默认认为这个propertyName的首字母是在定义getter的时候被写成大写的,现在要转成小写:
propertyName = Character.toLowerCase(methodName.charAt(3)) + methodName.substring(4);
结果,正确的TEST就被转成错误的tEST了。
解决:
解决这个问题比较简单,在转换前,多加一行代码:
TypeUtils.compatibleWithJavaBean =true;
此时,fastjson会先判断propertyName长度大于1、且头两个字符都是大写时,不做转换:
if(name.length() > 1
&& Character.isUpperCase(name.charAt(1))
&& Character.isUpperCase(name.charAt(0))){
return name;
}
也就是说,连续大写开头的propertyName,在转换时,会保持原样。
坑0x02 第一个字符大写,第二个字符不是大写的键名
按坑0x01里面讲的设置TypeUtils.compatibleWithJavaBean=true
后,如果有如下的键名,依然会出问题:
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挖的坑。
如上所述,就算你按坑0x01、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,就不要用lombok生成的getter/setter了,自己写,保持首字符小写,如:getiPhone/setiPhone,只要在代码里面有这两个方法,lombok就不会自动生成,lombok是按忽略大小写后的propertyName判断的。
- 把propertyName第二个字符改成小写,或者重新取个名字,并用@JSONField注明正确的名称,如:
@Getter
@ JSONField(name = "iPhone")
private String iphone = "1";
网友评论