Gson 快速開始!
Gson是谷歌搞出來的用來解析json的工具,運用了java的反射機制,他的功能主要有:
- 將java對象解析成json對象
- 將json對象解析成java對象
使用時需要在maven工程中導入相應依賴
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.1</version>
</dependency>
or
compile 'com.google.code.gson:gson:2.3.1'
解析
解析情況分爲三種:
- 標量(Scalar),也就是單純的字符串或則數字形式
- 序列(Sequence),也就是若干數據按照一定順序並列在一起又稱“數組”
- 映射(Mapping),也就是key/value鍵值對
JsonObject對象
將一個json串解析成一個JsonObject對象,對於獲取其中一些簡單的屬性比較方便,免去了寫映射類了
// json:{"tasks":[{"id":"123","name","pp"},{"id":"456","name","mm"}]}
JsonObject workInfoObj = gson.fromJson(workInfoObjStr,JsonObject.class);
String workTaskId = workInfoObj.get("tasks").getAsJsonArray().get(0).getAsJsonObject().get("id").getAsString();
JsonObject方法詳情查看:https://www.javadoc.io/doc/com.google.code.gson/gson/latest/com.google.gson/com/google/gson/JsonObject.html
普通對象
採用映射方式,將JavaBean對象的屬性值與json對象進行映射,注意:不匹配的屬性不會被映射,字段值必須一樣!
String json_str = "{"name":"kalen", "age":22}";
Gson gson = new Gson();
User user = gson.fromGson(json_str, User.class);
數組對象
//數組對應gson中的類型
Type listType = new TypeToken<List<String>>() {}.getType();
//gson需要的轉換對象或則數據來源
List<String> target = new LinkedList<String>();
target.add("blah");
Gson gson = new Gson();
// 將list對象轉爲json對象
String json = gson.toJson(target, listType);
// 將json對象轉爲list對象
List<String> target2 = gson.fromJson(json, listType);
此處使用了泛型!
注意:當解析非基本數據類型和數組時,需要加上泛型!
Gson gson = new Gson();
String jsonArray = "[\"Android\",\"Java\",\"PHP\"]";
String[] strings = gson.fromJson(jsonArray, String[].class);
但對於List將上面的代碼中的 String[].class
直接改爲 List.class
是行不通的。對於Java來說List
和List
這倆個的字節碼文件只一個那就是List.class
,這是Java泛型使用時要注意的問題 泛型擦除。
爲了解決的上面的問題,Gson爲我們提供了TypeToken
來實現對泛型的支持,所以當我們希望使用將以上的數據解析爲List
時需要這樣寫。
Gson gson = new Gson();
String jsonArray = "[\"Android\",\"Java\",\"PHP\"]";
String[] strings = gson.fromJson(jsonArray, String[].class);
List<String> stringList = gson.fromJson(jsonArray, new TypeToken<List<String>>() {}.getType());
注:TypeToken
的構造方法是protected
修飾的,所以上面纔會寫成new TypeToken>() {}.getType()
而不是 new TypeToken>().getType()
泛型解析對接口POJO的設計影響
泛型的引入可以減少無關的代碼,如我現在所在公司接口返回的數據分爲兩類:
{"code":"0","message":"success","data":{}}
{"code":"0","message":"success","data":[]}
我們真正需要的data
所包含的數據,而code
只使用一次,message
則幾乎不用。如果Gson不支持泛型或不知道Gson支持泛型的同學一定會這麼定義POJO。
public class UserResponse {
public int code;
public String message;
public User data;
}
當其它接口的時候又重新定義一個XXResponse
將data
的類型改成XX,很明顯code
,和message
被重複定義了多次,通過泛型的話我們可以將code
和message
字段抽取到一個Result
的類中,這樣我們只需要編寫data
字段所對應的POJO即可,更專注於我們的業務邏輯。如:
public class Result<T> {
public int code;
public String message;
public T data;
}
// 使用
// 不再重複定義Result類
Type userType = new TypeToken<Result<User>>(){}.getType();
Result<User> userResult = gson.fromJson(json,userType);
User user = userResult.data;
Type userListType = new TypeToken<Result<List<User>>>(){}.getType();
Result<List<User>> userListResult = gson.fromJson(json,userListType);
List<User> users = userListResult.data;
那麼對於data
字段是User
時則可以寫爲 Result
,當是個列表的時候爲 Result>
,其它同理。
對於泛型封裝可以參考: 《搞定Gson泛型封裝》
自定義解析
由於js和java的命名方式不同,如js採用下劃線_
連接命名而java採用駝峯命名,此時前後端的字段無法對應,導致映射失敗,可以採用自定義解析方式,規定規則,或者採用註解方式。
GsonBuilder
定製gson實例對象中的解析方式
Gson gson = new GsonBuilder()
.registerTypeAdapter(Id.class, new IdTypeAdapter())
.enableComplexMapKeySerialization()
.serializeNulls()
.setDateFormat(DateFormat.LONG)
.setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE)//會把字段首字母大寫
.setPrettyPrinting()
.setVersion(1.0)
.create();
此處我們關注setFieldNamingPolicy
方法即可,傳入解析命名規則:
Enum Constant | Description |
---|---|
IDENTITY |
原樣解析 |
LOWER_CASE_WITH_DASHES |
將Java駝峯規則轉換爲小寫用 - 連接的字段來映射 |
LOWER_CASE_WITH_DOTS |
將Java駝峯規則轉換爲小寫用 . 連接的字段來映射 |
LOWER_CASE_WITH_UNDERSCORES |
將Java駝峯規則轉換爲小寫用 _ 連接的字段來映射 |
UPPER_CAMEL_CASE |
將Java駝峯規則轉換爲首字母也大寫的規則 |
UPPER_CAMEL_CASE_WITH_SPACES |
將Java駝峯規則轉換爲首字母也大寫,且使用空格分割 |
其餘屬性均可在該網站查詢到:
- setFieldNamingPolicy 設置序列字段的命名策略(UPPER_CAMEL_CASE,UPPER_CAMEL_CASE_WITH_SPACES,LOWER_CASE_WITH_UNDERSCORES,LOWER_CASE_WITH_DASHES)
- addDeserializationExclusionStrategy 設置反序列化時字段採用策略ExclusionStrategy,如反序列化時不要某字段,當然可以採用@Expore代替。
- excludeFieldsWithoutExposeAnnotation 設置沒有@Expore則不序列化和反序列化
- addSerializationExclusionStrategy 設置序列化時字段採用策略,如序列化時不要某字段,當然可以採用@Expore代替。
- registerTypeAdapter 爲某特定對象設置固定的序列和反序列方式,實現JsonSerializer和JsonDeserializer接口
- setFieldNamingStrategy 設置字段序列和反序列時名稱顯示,也可以通過@Serializer代替
- setPrettyPrinting 設置gson轉換後的字符串爲一個比較好看的字符串
- setDateFormat 設置默認Date解析時對應的format格式
@Expose註解
如果採用new Gson()方式創建Gson則@Expose則沒有任何效果,若採用GsonBuilder創建Gson並且調用了excludeFieldsWithoutExposeAnnotation則@Expose將會影響toJson和fromGson序列化和反序列化數據。
public class User {
@Expose private String firstName;
@Expose(serialize = false) private String lastName;
@Expose(serialize = false, deserialize = false) private String emailAddress;
private String password;
}
例子中password不管是toJson還是fromJson都不會用到,emailAddress和lastName在序列化(fromJson)時將被採用,emailAddress在反序列化(toJson)時將不被採用。
@SerializedName註解
該註解標註在JavaBean的字段上,用於候選對應解析的字段
@SerializedName 註解解決 JSON 字符串鍵名稱和 POJO 類屬性名不對應的問題
// 如果json字符串爲
String jsonStr="{'my_name':'pp'}";
// JavaBean中name字段爲
@SerializedName("my_name")
private String myName;
SerializedName
註解提供了兩個屬性,上面用到了其中一個,別外還有一個屬性alternate
,接收一個String數組。
注:alternate
需要2.4版本
@SerializedName(value = "emailAddress", alternate = {"email", "email_address"})
public String emailAddress;
注意:當上面的三個屬性(email_address、email、emailAddress)都中出現任意一個時均可以得到正確的結果。
注:當多種情況同時出時,以最後一個出現的值爲準。
解析複雜對象
參考:https://www.jianshu.com/p/185e1ee9f05b
本文參考資料:
https://www.jianshu.com/p/31396863d1aa
https://www.javadoc.io/doc/com.google.code.gson/gson/latest/com.google.gson/com/google/gson/FieldNamingPolicy.html
https://www.jianshu.com/p/558844c96fc1
https://github.com/google/gson/blob/master/UserGuide.md
https://www.jianshu.com/p/e740196225a4