Gson是Google開源的一個JSON庫,被普遍應用在Android開發中。java
說明:如下全部的用法都基於Account類!git
public class Account {
private String uid;
private String userName;
private String password;
private String telNumber;
public Account(String uid, String userName, String telNumber) {
this.uid = uid;
this.userName = userName;
this.telNumber = telNumber;
}
@Override
public String toString() {
return "Account [uid=" + uid + ", userName=" + userName + ", password=" + password + ", telNumber=" + telNumber
+ "]";
}
}
複製代碼
dependencies {
implementation 'com.google.code.gson:gson:2.8.5'
}
複製代碼
Gson提供了兩種建立對象的方式:github
直接使用Gson構造方法建立;json
Gson gson = new Gson();數組
使用GsonBuilder建立;服務器
Gson gson = new GsonBuilder().create();ide
相比直接使用構造方法,GsonBuilder建立的方式更靈活,由於它支持對Gson的配置。ui
Account account = new Account("00001", "Freeman", "13000000000");
System.out.println(gson.toJson(account));
ArrayList<Account> accountList = new ArrayList<Account>();
accountList.add(account);
System.out.println(gson.toJson(accountList));
複製代碼
結果:this
{"uid":"00001","userName":"Freeman","telNumber":"13000000000"}
[{"uid":"00001","userName":"Freeman","telNumber":"13000000000"}]
複製代碼
因爲Java中的泛型存在類型擦除的問題,因此使用泛型接收JSON解析結果的時候有點特殊。google
String json = "{\"uid\":\"00001\",\"userName\":\"Freeman\",\"telNumber\":\"13000000000\"}";
Account receiveAccount = gson.fromJson(json, Account.class);
System.out.println(receiveAccount.toString());
複製代碼
結果:
Account [uid=00001, userName=Freeman, password=null, telNumber=13000000000]
複製代碼
String listJson = "[{\"uid\":\"00001\",\"userName\":\"Freeman\",\"telNumber\":\"13000000000\"}]";
List receiveAccountList = gson.fromJson(listJson, new TypeToken<List<Account>>(){}.getType());
System.out.println("receiveAccountList size = " + receiveAccountList.size());
複製代碼
結果:
receiveAccountList size = 1
複製代碼
在開發中有時會對Bean對象進行復用,但可能有幾個字段的命名和當前的對象不一致,這樣在解析JSON的時候就不能正確賦值。Gson提供了字段複用功能——@SerializedName,可用一個字段接收不一樣的JSON字段。
// json字符串中手機號的字段爲phone或telNumber時均可正確解析
@SerializedName("phone")
private String telNumber;
// json字符串中用戶名的字段爲userName、user_name、uname或u_name時均可正確解析
@SerializedName(value = "userName", alternate = {"user_name", "uname", "u_name"})
private String userName;
複製代碼
除了以上用法,Gson還提供了豐富的配置選項,包括:空值過濾,字段命名規則,自定義解析器,自定義序列化/反序列化等。
Gson默認狀況下會過濾空值字段,但有時在提交數據給後臺時,即使字段爲空,也須要傳給後臺,此時可經過GsonBuilder進行配置。
gson = new GsonBuilder().serializeNulls().create();
Account account = new Account("00001", "Freeman", "13000000000");
System.out.println(gson.toJson(account));
複製代碼
結果:
{"uid":"00001","userName":"Freeman","password":null,"phone":"13000000000"}
複製代碼
從結果能夠看出,password字段被輸出了,而前面直接建立Gson轉換的時候沒有輸出password字段。
由於不一樣的語言使用不一樣的命名規則,這會出現爲了正確解析JSON字符串,而使用不符合命名規則的字段名。如PHP使用小寫字母下劃線分割的命名方式,而Java使用駱駝命名的方式,這樣Android程序在接收JSON字符串的時候就須要使用小寫字母下劃線分割的命名方式,然而這並不符合Java的命名規範。爲了解決這個問題,Gson提供了豐富的字段命名規則。
// 默認的字段轉換規則,字段名不變
FieldNamingPolicy.IDENTITY()
// 將json中的字段名轉換爲首字母大寫的格式
FieldNamingPolicy.UPPER_CAMEL_CASE()
如:"user_name" -> "UserName"
// 將json中的字段名轉換爲首字母大寫,單詞之間以空格分割的格式
FieldNamingPolicy.UPPER_CAMEL_CASE_WITH_SPACES()
如:"user_name" -> "User Name"
// 將json中的字段名轉換爲小寫字母,單詞之間如下劃線分割的格式
FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES()
如:"UserName" -> "user_name"
// 將json中的字段名轉換爲小寫字母,單詞之間以分隔線分割的格式
FieldNamingPolicy.LOWER_CASE_WITH_DASHES()
如:"UserName" -> "user-name"
複製代碼
因此對於服務器是PHP的狀況,Android端可以使用FieldNamingPolicy.UPPER_CAMEL_CASE轉換規則。
Gson gson = new GsonBuilder()
.setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE)
.create();
複製代碼
固然,若是上面幾種命名轉換規則不知足需求,也可自定義命名轉換規則,只須要實現FieldNamingStrategy接口,並完善translateName方法便可。須要注意的是,命名轉換規則會同時被應用於序列化和反發序列化,因此在將對象轉換爲JSON(序列化)傳給後臺時須要注意。
默認狀況下,Gson將對象轉換爲JSON時,會將全部非null字段進行轉換,但有時爲了業務需求,一般會添加一些狀態字段,如表示item選中狀態的isSelected,這些字段一般不但願被轉換。Gson提供了多種過濾方式,這裏說說兩種最經常使用的。
經過修飾符過濾
Gson gson = new GsonBuilder() // 過濾transient或static修飾的字段, .excludeFieldsWithModifiers(Modifier.TRANSIENT, Modifier.STATIC) .create();
自定義過濾規則
經過實現setExclusionStrategies接口來定義過濾規則:
ExclusionStrategy strategy = new ExclusionStrategy() {
@Override
public boolean shouldSkipClass(Class<?> arg0) {
// 過濾指定的類
return false;
}
@Override
public boolean shouldSkipField(FieldAttributes arg0) {
// 過濾指定的字段
return false;
}
};
Gson gson = new GsonBuilder()
// 過濾規則同時適用於序列化/反序列化
.setExclusionStrategies(strategy)
// 過濾規則只適用於序列化
.addSerializationExclusionStrategy(strategy)
// 過濾規則只適用於反序列化
.addDeserializationExclusionStrategy(strategy)
.create();
複製代碼
字段容錯性主要表如今字段的類型不匹配,好比接收的字段是整型,返回的倒是字符串或者null, 這種狀況狀況直接使用String接收整型倒能夠解決,但對於接收類型是數組,返回的確是對象的狀況,卻不能直接使用類型兼容來處理。
雖然這些狀況都是由於後臺數據格式的問題,但對用戶最直觀的感覺是APP閃退了。因此咱們須要對JSON的解析過程作必定的兼容處理,以防出現異常數據時致使APP閃退。
開發中常常遇到的類型兼容問題彙總:
Gson提供了多種序列化/反序列化方式,因爲這裏咱們只作JSON解析(即反序列化)的兼容處理,因此直接實現JsonDeserializer接口便可。
// 爲數組類型自定義反序列化適配器
JsonDeserializer<List<?>> listDeserial = new JsonDeserializer<List<?>>() {
@Override
public List<?> deserialize(JsonElement arg0, java.lang.reflect.Type arg1,
JsonDeserializationContext arg2) throws JsonParseException {
if (arg0.isJsonArray()) {
JsonArray jsonArray = arg0.getAsJsonArray();
if (jsonArray.size() == 0) {
return Collections.EMPTY_LIST;
}
List<?> resultList = new ArrayList<>();
for (JsonElement element : jsonArray) {
resultList.add(arg2.deserialize(element, arg1));
}
return resultList;
} else {
return Collections.EMPTY_LIST;
}
}
};
Gson gson = new GsonBuilder()
// 註冊自定義的反序列化適配器
.registerTypeHierarchyAdapter(List.class, listDeserial)
.create();
複製代碼
gson = new GsonBuilder()
// 不過濾空值
.serializeNulls()
// 設置字段命名轉換規則
.setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE)
// 設置字段序列化/反序列化過濾規則
.excludeFieldsWithModifiers(Modifier.TRANSIENT, Modifier.STATIC)
// 自定義類型解析器,提升Gson容錯性
.registerTypeHierarchyAdapter(List.class, listDeserial)
.create();
複製代碼
以上只是使用Gson過程當中的一些總結,其中涉及的的不少細節,如JSON解析的過程,類型的匹配等都沒有說起,感興趣的能夠閱讀其源碼。