GsonQuickStart

Gson 快速開始!

Gson是谷歌搞出來的用來解析json的工具,運用了java的反射機制,他的功能主要有:

  1. 將java對象解析成json對象
  2. 將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'

解析

解析情況分爲三種:

  1. 標量(Scalar),也就是單純的字符串或則數字形式
  2. 序列(Sequence),也就是若干數據按照一定順序並列在一起又稱“數組”
  3. 映射(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來說ListList 這倆個的字節碼文件只一個那就是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;
}

當其它接口的時候又重新定義一個XXResponsedata的類型改成XX,很明顯code,和message被重複定義了多次,通過泛型的話我們可以將codemessage字段抽取到一個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駝峯規則轉換爲首字母也大寫,且使用空格分割

其餘屬性均可在該網站查詢到:

  1. setFieldNamingPolicy 設置序列字段的命名策略(UPPER_CAMEL_CASE,UPPER_CAMEL_CASE_WITH_SPACES,LOWER_CASE_WITH_UNDERSCORES,LOWER_CASE_WITH_DASHES)
  2. addDeserializationExclusionStrategy 設置反序列化時字段採用策略ExclusionStrategy,如反序列化時不要某字段,當然可以採用@Expore代替。
  3. excludeFieldsWithoutExposeAnnotation 設置沒有@Expore則不序列化和反序列化
  4. addSerializationExclusionStrategy 設置序列化時字段採用策略,如序列化時不要某字段,當然可以採用@Expore代替。
  5. registerTypeAdapter 爲某特定對象設置固定的序列和反序列方式,實現JsonSerializer和JsonDeserializer接口
  6. setFieldNamingStrategy 設置字段序列和反序列時名稱顯示,也可以通過@Serializer代替
  7. setPrettyPrinting 設置gson轉換後的字符串爲一個比較好看的字符串
  8. 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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章