有一位同事說使用 fastjson 進行 JSON 序列化存儲到資料庫後,發現 JSON 字元串“莫名其妙地”多了一些屬性!幫看了下代碼,看到基本類型的布爾類型以 is 開頭的屬性,再看到 fastjson ,就有點想笑。 ## 復現 定義 MyClass ``` public class MyCl ...
有一位同事說使用 fastjson 進行 JSON 序列化存儲到資料庫後,發現 JSON 字元串“莫名其妙地”多了一些屬性!幫看了下代碼,看到基本類型的布爾類型以 is 開頭的屬性,再看到 fastjson ,就有點想笑。
復現
定義 MyClass
public class MyClass {
// boolean 類型的屬性
private boolean isActive;
private boolean valid;
// int 類型的屬性
private int id;
// 預設構造器
public MyClass() {
}
// 帶有所有屬性的構造器
public MyClass(boolean isActive, boolean valid, int id) {
this.isActive = isActive;
this.valid = valid;
this.id = id;
}
// isActive 的 getter 和 setter 方法
public boolean isActive() {
return isActive;
}
public void setActive(boolean isActive) {
this.isActive = isActive;
}
// valid 的 getter 和 setter 方法
public boolean getValid() {
return valid;
}
public void setValid(boolean valid) {
this.valid = valid;
}
// id 的 getter 和 setter 方法
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
編寫測試代碼:
import com.alibaba.fastjson.JSON;
public class MyClassMain {
public static void main(String[] args) {
// 創建 MyClass 對象
MyClass myClass = new MyClass(true, false, 123);
// 使用 fastjson 序列化對象
String jsonString = JSON.toJSONString(myClass);
// 列印 JSON 字元串
System.out.println(jsonString);
}
}
結果:
{“active”:true,“id”:123,“valid”:false}
我們發現多了一個 active 屬性,少了一個 isActive 屬性!
推薦一個開源免費的 Spring Boot 實戰項目:
分析
通過調試可以發現,問題出現在下麵這個函數:
com.alibaba.fastjson.serializer.SerializeConfig#createJavaBeanSerializer(java.lang.Class<?>)
public final ObjectSerializer createJavaBeanSerializer(Class<?> clazz) {
String className = clazz.getName();
long hashCode64 = TypeUtils.fnv1a_64(className);
if (Arrays.binarySearch(denyClasses, hashCode64) >= 0) {
throw new JSONException("not support class : " + className);
}
// 關鍵
SerializeBeanInfo beanInfo = TypeUtils.buildBeanInfo(clazz, null, propertyNamingStrategy, fieldBased);
if (beanInfo.fields.length == 0 && Iterable.class.isAssignableFrom(clazz)) {
return MiscCodec.instance;
}
return createJavaBeanSerializer(beanInfo);
}
而 buildBeanInfo 的關鍵是com.alibaba.fastjson.util.TypeUtils#computeGetters
public static List<FieldInfo> computeGetters(Class<?> clazz, //
JSONType jsonType, //
Map<String,String> aliasMap, //
Map<String,Field> fieldCacheMap, //
boolean sorted, //
PropertyNamingStrategy propertyNamingStrategy //
){
// 省略部分代碼
if(methodName.startsWith("is")){
if(methodName.length() < 3){
continue;
}
if(returnType != Boolean.TYPE
&& returnType != Boolean.class){
continue;
}
char c2 = methodName.charAt(2);
String propertyName;
Field field = null;
if(Character.isUpperCase(c2)){
if(compatibleWithJavaBean){
propertyName = decapitalize(methodName.substring(2));
} else{
propertyName = Character.toLowerCase(methodName.charAt(2)) + methodName.substring(3);
}
// 這裡 isActive 的屬性名被計算出 active
propertyName = getPropertyNameByCompatibleFieldName(fieldCacheMap, methodName, propertyName, 2);
}
// 省略其他
JSONField fieldAnnotation = null;
if(field != null){
fieldAnnotation = TypeUtils.getAnnotation(field, JSONField.class);
if(fieldAnnotation != null){
if(!fieldAnnotation.serialize()){
continue;
}
ordinal = fieldAnnotation.ordinal();
serialzeFeatures = SerializerFeature.of(fieldAnnotation.serialzeFeatures());
parserFeatures = Feature.of(fieldAnnotation.parseFeatures());
if(fieldAnnotation.name().length() != 0){
//關鍵: 使用 JSONField 註解設置的 name 替代屬性名
propertyName = fieldAnnotation.name();
if(aliasMap != null){
propertyName = aliasMap.get(propertyName);
if(propertyName == null){
continue;
}
}
}
if(fieldAnnotation.label().length() != 0){
label = fieldAnnotation.label();
}
}
}
// 省略部分代碼
FieldInfo fieldInfo = new FieldInfo(propertyName, method, field, clazz, null, ordinal, serialzeFeatures, parserFeatures,
annotation, fieldAnnotation, label);
fieldInfoMap.put(propertyName, fieldInfo);
}
}
Field[] fields = clazz.getFields();
computeFields(clazz, aliasMap, propertyNamingStrategy, fieldInfoMap, fields);
return getFieldInfos(clazz, sorted, fieldInfoMap);
}
其實 fastjson 通過反射雖然有能力識別真實的屬性名,但是實際操作時會根據 getter 方法反推出屬性名,造成轉為 JSON 字元串時和實際屬性名存在偏差。
解決辦法
遵循阿裡巴巴 Java 開發手冊
孤盡老師的《Java 開發手冊》 中專門強調任何布爾類型的變數都不要加 is 首碼,基本類型布爾屬性反向解析時,會誤以為不帶 is 導致獲取不到屬性,拋出異常。關註公眾號:Java核心技術,回覆:手冊,可獲取高清完整版。
使用別名
使用 fastjson 自帶的 @JSONField 註解,不過這種方式 fastjson 的侵入性太強。
public class MyClass {
@JSONField( name="isActive")
// boolean 類型的屬性
private boolean isActive;
private boolean valid;
// 省略其他
}
總結
我認為 對於 Java 程式員而言,《阿裡巴巴 Java 開發手冊》至少讀 3 遍。 工作中發現太多常見低級問題都是 《阿裡巴巴 Java 開發手冊》已經存在的問題。關註公眾號:Java核心技術,回覆:手冊,可獲取高清完整版。
然而推薦很多次《阿裡巴巴 Java 開發手冊》雖然很薄,但是很多人還是不會認真閱讀幾遍,導致在相同的地方跌倒很多遍。哪怕遇到類似的問題,也很容易快速想出原因。
我們遇到問題時,一定不要止步於解決問題,而是應該尋找最合理的解決方案。比如雖然加上 @JSONField
可以“解決問題”,但侵入性太強,假如其他人也用這個對象使用其他 JSON 序列化工具,就會出問題,這並不是一個好的方案。
AI 時代,遇到問題自己如果不能快速解決時,可以考慮尋求 AI 的幫助。不過使用 AI 時一定要將問題交代清楚。很多同學說的問題連其他同事都聽不懂,更不別說 AI 了。
版權聲明:本文為CSDN博主「明明如月學長」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。原文鏈接:https://blog.csdn.net/w605283073/article/details/131270338
近期熱文推薦:
1.1,000+ 道 Java面試題及答案整理(2022最新版)
4.別再寫滿屏的爆爆爆炸類了,試試裝飾器模式,這才是優雅的方式!!
覺得不錯,別忘了隨手點贊+轉發哦!