Gson 解析之 - 如何讓 json 鍵和實體類的屬性名不一樣
最近Gson用的比較多,用的時候一直有一個疑問,難道本地的實體類的屬性名一定要和Json的鍵一一對應嗎?
json數據
{
"name": "wangzhen",
"age": 20,
"tall": "176",
"sex": "male"
}
對應的實體類
public class Person1 {
String name;
String age;
String sex;
String tall;
}
一切看起來都是那麼的美好,但是如果有一天,我們從另一個接口上,請求下來的數據是這樣的呢?
{
"person_name": "wangzhen",
"person_age": 20,
"tall": "176",
"person_sex": "male"
}
那我們的實體類豈不是要寫成這樣?
public class Person2 {
String first_name;
String person_name;
String person_age;
String person_sex;
}
可以試一下Person1
Gson gson = new Gson();
InputStream inputStream = null;
inputStream = new FileInputStream(new File("json file path"));
InStream2String converter = new InStream2String();
String json = converter.convert(inputStream);
Person person = gson.fromJson(json, Person1.class);
System.out.println(person);
=== output ===
Person1{name='null', age='null', sex='null', tall='176'}
簡直不能忍
可是有什麼辦法嗎?想到的一個解決方案就是爲每一個實體類的屬性添加一個註解,在註解中標明該屬性對應json的值,比如上面的 json 的 person_name 應該對應 Person 的 name 屬性。
有思路就簡單咯,自己動手豐衣足食
新建一個註解,就叫Map吧
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Map {
String value();
}
現在的屬性應該是這個樣子的,有點感覺了。
@Map("person_name") String name;
@Map("person_age") String age;
@Map("person_sex") String sex;
在運行時,只需要動態獲取註解裏面的值,就可以得到當前屬性應當對應的json的鍵,所以就可以起到屬性名與json鍵不同的效果了
好了,只需要去 818 Gson的源碼,然後用一個裝飾模式爲Gson動態的添加一個職責差不多咯
但是! too young, too naive!
跟蹤源碼的時候發現,會調用com.google.gson.internal.bind.ReflectiveTypeAdapterFactory
這個類中的 這個方法
private String getFieldName(Field f) {
SerializedName serializedName = f.getAnnotation(SerializedName.class);
return serializedName == null ? fieldNamingPolicy.translateName(f) : serializedName.value();
}
尼瑪,這 SerializedName
又是什麼鬼。。
點進去看一下
SerializedName
註解的源碼:
/**
* An annotation that indicates this member should be serialized to JSON with
* the provided name value as its field name.
*
* <p>This annotation will override any {@link com.google.gson.FieldNamingPolicy}, including
* the default field naming policy, that may have been set on the {@link com.google.gson.Gson}
* instance. A different naming policy can set using the {@code GsonBuilder} class. See
* {@link com.google.gson.GsonBuilder#setFieldNamingPolicy(com.google.gson.FieldNamingPolicy)}
* for more information.</p>
*
* <p>Here is an example of how this annotation is meant to be used:</p>
* <pre>
* public class SomeClassWithFields {
* @SerializedName("name") private final String someField;
* private final String someOtherField;
*
* public SomeClassWithFields(String a, String b) {
* this.someField = a;
* this.someOtherField = b;
* }
* }
* </pre>
*
* <p>The following shows the output that is generated when serializing an instance of the
* above example class:</p>
* <pre>
* SomeClassWithFields objectToSerialize = new SomeClassWithFields("a", "b");
* Gson gson = new Gson();
* String jsonRepresentation = gson.toJson(objectToSerialize);
* System.out.println(jsonRepresentation);
*
* ===== OUTPUT =====
* {"name":"a","someOtherField":"b"}
* </pre>
*
* <p>NOTE: The value you specify in this annotation must be a valid JSON field name.</p>
*
* @see com.google.gson.FieldNamingPolicy
*
* @author Inderjeet Singh
* @author Joel Leitch
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface SerializedName {
/**
* @return the desired name of the field when it is serialized
*/
String value();
}
這。。。。。
結合上面的getFieldName
方法,Gson在獲取屬性名的時候會先判斷SerializedName
註解的值,如果有就使用 SerializedName
的值(value)作爲屬性名。
好了,輪子造到一半讓Google給截胡了。但,省了很多事啊哈哈哈哈哈哈哈哈。
忽略上面的笑聲
好了,你的實體類的屬性現在應該長這模樣,至於其他的,小夥伴們一起探索咯
@SerializedName("person_name") String name;
@SerializedName("person_age") String age;
@SerializedName("person_sex") String sex;
轉自:
作者:氫電公敵
鏈接:https://www.jianshu.com/p/867798e8b627