第三方類庫-JSON-Google Gson

原文出處:http://www.jianshu.com/p/fc5c9cdf3aab


前言

最近在研究Retrofit中使用的Gson的時候,發現對Gson的一些深層次的概念和使用比較模糊,所以這裏做一個知識點的歸納整理。

Gson(又稱Google Gson)是Google公司發佈的一個開放源代碼的Java庫,主要用途爲序列化Java對象爲JSON字符串,或反序列化JSON字符串成Java對象。而JSON(JavaScript Object Notation) 是一種輕量級的數據交換格式,易於人閱讀和編寫,同時也易於機器解析和生成,廣泛應用於各種數據的交互中,尤其是服務器與客戶端的交互。


基本概念

  • Serialization:序列化,使Java對象到Json字符串的過程。
  • Deserialization:反序列化,字符串轉換成Java對象。
  • JSON數據中的JsonElement有下面這四種類型:
    JsonPrimitive —— 例如一個字符串或整型
    JsonObject—— 一個以 JsonElement 名字(類型爲 String)作爲索引的集合。也就是說可以把 JsonObject 看作值爲 JsonElement 的鍵值對集合。
    JsonArray—— JsonElement 的集合。注意數組的元素可以是四種類型中的任意一種,或者混合類型都支持。
    JsonNull—— 值爲null

Gson解決的問題

  1. 提供一種像toString()和構造方法的很簡單的機制,來實現Java 對象和Json之間的互相轉換。

  2. 允許已經存在的無法改變的對象,轉換成Json,或者Json轉換成已存在的對象。

  3. 允許自定義對象的表現形式

  4. 支持任意的複雜對象

  5. 能夠生成可壓縮和可讀的Json的字符串輸出。


Gson處理對象的幾個重要點

1 推薦把成員變量都聲明稱private的

2 沒有必要用註解(@Expose 註解)指明某個字段是否會被序列化或者反序列化,所有包含在當前類(包括父類)中的字段都應該默認被序列化或者反序列化

3 如果某個字段被 transient 這個Java關鍵詞修飾,就不會被序列化或者反序列化

4 下面的實現方式能夠正確的處理null
1)當序列化的時候,如果對象的某個字段爲null,是不會輸出到Json字符串中的。
2)當反序列化的時候,某個字段在Json字符串中找不到對應的值,就會被賦值爲null

5 如果一個字段是 synthetic
的,他會被忽視,也即是不應該被序列化或者反序列化

6 內部類(或者anonymous class(匿名類),或者local class(局部類,可以理解爲在方法內部聲明的類))的某個字段和外部類的某個字段一樣的話,就會被忽視,不會被序列化或者反序列化


Gson中的一些註解

1 @SerializedName註解

該註解能指定該字段在JSON中對應的字段名稱

public class Box {

  @SerializedName("w")
  private int width;

  @SerializedName("h")
  private int height;

  @SerializedName("d")
  private int depth;

  // Methods removed for brevity
}

也就是說{"w":10,"h":20,"d":30} 這個JSON 字符串能夠被解析到上面的width,height和depth字段中。

2 @Expose註解

該註解能夠指定該字段是否能夠序列化或者反序列化,默認的是都支持(true)。

public class Account {

  @Expose(deserialize = false)
  private String accountNumber;

  @Expose
  private String iban;

  @Expose(serialize = false)
  private String owner;

  @Expose(serialize = false, deserialize = false)
  private String address;

  private String pin;
}

需要注意的通過 builder.excludeFieldsWithoutExposeAnnotation()方法是該註解生效。

  final GsonBuilder builder = new GsonBuilder();
    builder.excludeFieldsWithoutExposeAnnotation();
    final Gson gson = builder.create();
3 @Since和@Until註解

Since代表“自從”,Until 代表”一直到”。它們都是針對該字段生效的版本。比如說 @Since(1.2)代表從版本1.2之後才生效,@Until(0.9)代表着在0.9版本之前都是生效的。

public class SoccerPlayer {

  private String name;

  @Since(1.2)
  private int shirtNumber;

  @Until(0.9)
  private String country;

  private String teamName;

  // Methods removed for brevity
}

也就是說我們利用方法builder.setVersion(1.0)定義版本1.0,如下:

 final GsonBuilder builder = new GsonBuilder();
    builder.setVersion(1.0);

    final Gson gson = builder.create();

    final SoccerPlayer account = new SoccerPlayer();
    account.setName("Albert Attard");
    account.setShirtNumber(10); // Since version 1.2
    account.setTeamName("Zejtun Corinthians");
    account.setCountry("Malta"); // Until version 0.9

    final String json = gson.toJson(account);
    System.out.printf("Serialised (version 1.0)%n  %s%n", json);

由於shirtNumbercountry作用版本分別是1.2之後,和0.9之前,所以在這裏都不會得到序列化,所以輸出結果是:

Serialised (version 1.0)
  {"name":"Albert Attard","teamName":"Zejtun Corinthians"}

Gson 序列化

英文Serialize和format都對應序列化,這是一個Java對象到JSON字符串的過程。
接着看一個例子,下面分別是java類和以及我們期望的JSON數據:

public class Book {
  private String[] authors;
  private String isbn10;
  private String isbn13;
  private String title;
  //爲了代碼簡潔,這裏移除getter和setter方法等

}
{
  "title": "Java Puzzlers: Traps, Pitfalls, and Corner Cases",
  "isbn-10": "032133678X",
  "isbn-13": "978-0321336781",
  "authors": [
    "Joshua Bloch",
    "Neal Gafter"
  ]
}

你肯定能發現JSON數據中出現了isbn-10isbn-13, 我們怎麼把字段數據isbn10isbn13轉化爲JSON數據需要的isbn-10isbn-13,Gson當然爲我們提供了對應的解決方案

1 序列化方案1

採用上面提到的@SerializedName註解。

public class Book {
  private String[] authors;

  @SerializedName("isbn-10")
  private String isbn10;

  @SerializedName("isbn-13")
  private String isbn13;
  private String title;
  //爲了代碼簡潔,這裏移除getter和setter方法等

}
2 序列化方案2

利用JsonSerializer

public class BookSerialiser implements JsonSerializer {

    @Override
    public JsonElement serialize(final Book book, final Type typeOfSrc, final JsonSerializationContext context) {

        final JsonObject jsonObject = new JsonObject();
        jsonObject.addProperty("title", book.getTitle());
        jsonObject.addProperty("isbn-10", book.getIsbn10());
        jsonObject.addProperty("isbn-13", book.getIsbn13());

        final JsonArray jsonAuthorsArray = new JsonArray();
        for (final String author : book.getAuthors()) {
            final JsonPrimitive jsonAuthor = new JsonPrimitive(author);
            jsonAuthorsArray.add(jsonAuthor);
        }
        jsonObject.add("authors", jsonAuthorsArray);

        return jsonObject;
    }
}

下面對序列化過程進行大致的分析:

  • JsonSerializer是一個接口,我們需要提供自己的實現,來滿足自己的序列化要求。

    public interface JsonSerializer<T> {
    
    /**
     *Gson 會在解析指定類型T數據的時候觸發當前回調方法進行序列化
     *
     * @param T 需要轉化爲Json數據的類型,對應上面的Book
     * @return 返回T指定的類對應JsonElement
     */
    public JsonElement serialize(T src, Type typeOfSrc, JsonSerializationContext context);
    }
  • 首先在上面的代碼中,我們需要創建的是一個JsonElement對象,這裏對應Book是一個對象,所以創建一個JsonObject類型。
    final JsonObject jsonObject = new JsonObject();
  • 然後我們將相應字段裏面的數據填充到jsonObject裏面。
    jsonObject.addProperty...
    jsonObject.add...
    下面是jsonObject中的添加方法:

  • 所以最後返回的還是一個JsonElement 類型,這裏對應的是jsonObject。完成了javaBean->JSON數據的轉化。

  • 同樣需要配置,

    // Configure GSON
      final GsonBuilder gsonBuilder = new GsonBuilder();
      gsonBuilder.registerTypeAdapter(Book.class, new BookSerialiser());
      gsonBuilder.setPrettyPrinting();
      final Gson gson = gsonBuilder.create();
    
      final Book javaPuzzlers = new Book();
      javaPuzzlers.setTitle("Java Puzzlers: Traps, Pitfalls, and Corner Cases");
      javaPuzzlers.setIsbn10("032133678X");
      javaPuzzlers.setIsbn13("978-0321336781");
      javaPuzzlers.setAuthors(new String[] { "Joshua Bloch", "Neal Gafter" });
    
      // Format to JSON
      final String json = gson.toJson(javaPuzzlers);
      System.out.println(json);

    ,這裏對應的是
    gsonBuilder.registerTypeAdapter(Book.class, new BookSerialiser())方法進行JsonSerializer的配置。在上面例子中,通過調用gsonBuilder.setPrettyPrinting();方法還告訴了 Gson 對生成的 JSON 對象進行格式化


Gson 反序列化

英文parse和deserialise對應反序列化,這是一個字符串轉換成Java對象的過程。
我們同樣採用上面一小節的代碼片段,只不過現在我們需要做的是將:

{
  "title": "Java Puzzlers: Traps, Pitfalls, and Corner Cases",
  "isbn-10": "032133678X",
  "isbn-13": "978-0321336781",
  "authors": [
    "Joshua Bloch",
    "Neal Gafter"
  ]
}

轉化爲對應的Book實體類,

1 反序列化方案1

利用@SerializedName 註解
也就是說我們的實體類Book.java可以這麼寫:

public class Book {
  private String[] authors;

  @SerializedName("isbn-10")
  private String isbn10;

  @SerializedName(value = "isbn-13", alternate = {"isbn13","isbn.13"})
  private String isbn13;
  private String title;
  //爲了代碼簡潔,這裏移除getter和setter方法等

}

可以看到這裏我們在@SerializedName 註解使用了一個value, alternate字段,value也就是默認的字段,對序列化和反序列化都有效,alternate只有反序列化纔有效果。也就是說一般服務器返回給我們JSON數據的時候可能同樣的一個圖片,表示"image","img","icon"等,我們利用@SerializedName 中的alternate字段就能解決這個問題,全部轉化爲我們實體類中的圖片字段。

2 反序列化方案2

我們在序列化的時候使用的是JsonSerialize ,這裏對應使用JsonDeserializer
我們將解析到的json數據傳遞給Book的setter方法即可。

public class BookDeserializer implements JsonDeserializer<Book> {

  @Override
  public Book deserialize(final JsonElement json, final Type typeOfT, final JsonDeserializationContext context)
      throws JsonParseException {
    final JsonObject jsonObject = json.getAsJsonObject();

    final JsonElement jsonTitle = jsonObject.get("title");
    final String title = jsonTitle.getAsString();

    final String isbn10 = jsonObject.get("isbn-10").getAsString();
    final String isbn13 = jsonObject.get("isbn-13").getAsString();

    final JsonArray jsonAuthorsArray = jsonObject.get("authors").getAsJsonArray();
    final String[] authors = new String[jsonAuthorsArray.size()];
    for (int i = 0; i < authors.length; i++) {
      final JsonElement jsonAuthor = jsonAuthorsArray.get(i);
      authors[i] = jsonAuthor.getAsString();
    }

    final Book book = new Book();
    book.setTitle(title);
    book.setIsbn10(isbn10);
    book.setIsbn13(isbn13);
    book.setAuthors(authors);
    return book;
  }
}

和Gson序列化章節一樣,我們這裏接着分析我們是怎麼將JSON數據解析(反序列化)爲實體類的:

  • 因爲我們可以發現上面的JSON數據是一個{}大括號包圍的,也就意味着這是一個Json對象。所以首先我們通過
    final JsonObject jsonObject = json.getAsJsonObject();將我們的JsonElement轉化爲JsonObject
  • 通過jsonObject.get("xxx").getAsString()的形式獲取相應String的值
  • 通過jsonObject.get("xx").getAsJsonArray();獲取相應的json數組,並遍歷出其中的相應字段值
  • 通過setter方法,將獲取到的值設置給Book類。
  • 最終返回的是 Book的對象實例。完成了JSON->javaBean的轉化
  • 同樣需要配置
  • 關於從本地流中讀取Json數據可以使用 InputStreamReader完成

    // Configure Gson
      GsonBuilder gsonBuilder = new GsonBuilder();
      gsonBuilder.registerTypeAdapter(Book.class, new BookDeserializer());
      Gson gson = gsonBuilder.create();
    
      // The JSON data
      try(Reader reader = new InputStreamReader(Main.class.getResourceAsStream("/part1/sample.json"), "UTF-8")){
    
        // Parse JSON to Java
        Book book = gson.fromJson(reader, Book.class);
        System.out.println(book);
      }


作者:CameloeAnthony
鏈接:http://www.jianshu.com/p/fc5c9cdf3aab
來源:簡書
著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。

發佈了3 篇原創文章 · 獲贊 3 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章