Gson——Gson序列化

轉自http://www.importnew.com/16638.html

簡單的例子

考慮下面這個 Java 對象。

public class Book {
 
  private String[] authors;
  private String isbn10;
  private String isbn13;
  private String title;
 
  // Methods removed for brevity
 
}

這個簡單的 Java 類封裝了一本書的屬性。假如我們需要將其序列化爲下面這個 JSON 對象。

{
  "title": "Java Puzzlers: Traps, Pitfalls, and Corner Cases",
  "isbn-10": "032133678X",
  "isbn-13": "978-0321336781",
  "authors": [
    "Joshua Bloch",
    "Neal Gafter"
  ]
}
Gson 不需要任何特殊配置就可以序列化 Book 類。Gson 使用 Java 字段名稱作爲 JSON 字段的名稱,並賦予對應的值。如果仔細地看一下上面的那個 JSON 示例會發現, ISBN 字段包含一個減號:isbn-10 和 isbn-13。不幸的是,使用默認配置不能將這些字段包含進來。解決問題的辦法之一就是使用註解,就像在這篇文章中描述的那樣:Gson 註解示例。使用註解可以自定義 JSON 字段的名稱,Gson將會以註解爲準進行序列化。另一個方法就是使用 JsonSerialiser (Java Doc),如下所示:

import java.lang.reflect.Type;
 
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
 
public class BookSerialiser implements JsonSerializer {
 
    @Override
    public JsonElement serialize(final Book book, final Type typeOfSrc, final JsonSerializationContext context) {
        final JsonObject jsonObject = new JsonObject();
        //The serialisation code is missing
 
        return jsonObject;
    }
}

上面的例子還缺失了重要部分,需要通過補充序列化代碼來完善。在添加更多代碼使其變得複雜之前,我們先來理解下這個類。

JsonSerializer 接口要求類型是將要進行序列化的對象類型。在這個例子中,我們要序列化的 Java 對象是 Book。serialize()方法的返回類型必須是一個 JsonElement (Java 文檔)類型的實例。詳見這篇文章:Gson 反序列化實例,下面列出了JsonElement 四種具體實現類型:

  • JsonPrimitive (Java Doc) —— 例如一個字符串或整型
  • JsonObject (Java Doc) —— 一個以 JsonElement 名字(類型爲 String)作爲索引的集合。類似於 Map<String,JsonElement>集合(Java Doc
  • JsonArray (Java Doc)—— JsonElement 的集合。注意數組的元素可以是四種類型中的任意一種,或者混合類型都支持。
  • JsonNull (Java Doc) —— 值爲null

JsonElement的類型


上面這張圖片展示了 JsonElement 的所有類型。可以把 JsonObject 看作值爲 JsonElement 的鍵值對集合。因此,這些值可以是另外四種對象

下面是序列化的完整實例:

import java.lang.reflect.Type;
 
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
import com.google.gson.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;
    }
}

我們在這裏添加了一些代碼。在理解整個圖片的含義前,我們先把它拆成一個個小的部分,先來解釋下每部分的含義。

如果要序列化這個 Java 對象,首先需要創建一個 JsonElement 實例。例子中是返回了一個 JsonObject 實例來代表 Book 對象,如下所示:

inal JsonObject jsonObject = new JsonObject();
該對象使用我們設置的字段名稱進行填充,如下:

// The variable 'book' is passed as a parameter to the serialize() method
    final JsonObject jsonObject = new JsonObject();
    jsonObject.addProperty("title", book.getTitle());
    jsonObject.addProperty("isbn-10", book.getIsbn10());
    jsonObject.addProperty("isbn-13", book.getIsbn13());

使用 addProperty() 方法 (Java Doc)可以添加任何 Java 原始類型以及String和Number。注意此處的 name 必須是唯一的,否則會被前一個覆蓋掉。可以將其看做是一個將字段名作爲值索引的 Map。

更復雜的對象,比如 Java 對象或數組就不能使用上面的方法來添加了。JsonObject 有另外一個 add() 方法,可以用來作爲替代,如下所示:

// The variable 'book' is passed as a parameter to the serialize() method
    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);

首先創建一個 JsonArray 對象,然後將所有 authors 添加進去。和 Java 不同的是,初始化 JsonArray 時不需要指定數組的大小。事實上,拋開這個類的名字不看,可以將 JsonArray 類更多地看做是一個 list 而非 array。最後將 jsonAuthorsArray  添加到 jsonObject 中。此處也可以在給 jsonAuthorsArray 添加元素之前,將其添加到 jsonObject 中。

在調用該序列化方法之前,我們需要將其註冊到 Gson 中:

import java.io.IOException;
 
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
 
public class Main {
 
  public static void main(final String[] args) throws IOException {
    // 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);
  }
}

通過註冊我們自己實現的序列化器,告訴 Gson 無論什麼時候序列化 Book 類型的對象都使用該序列化器進行序列化。

在上面例子中,通過調用 set prettying printing 方法還告訴了 Gson 對生成的 JSON 對象進行格式化,如下所示:

gsonBuilder.setPrettyPrinting();

雖然這對於調試和教程非常有用,但請不要在生產環境中這樣用,因爲可能會因此產生更大的 JSON 對象(文本的大小)。除此之外,由於 Gson 必須要格式化 JSON 對象,即對其進行相應的縮進,pretty printing 會有一些性能方面的消耗。

運行上面的代碼可以得到預期的 JSON 對象。對我們的第一個例子做個總結,即怎樣自定義 Gson 序列化器將 Java 對象序列化爲 JSON 對象。下一章將會講到怎樣使用 Gson 序列化嵌套對象。

嵌套對象

接下來的例子將會描述怎麼序列化嵌套對象。所謂嵌套對象是指在其它對象內部的對象。在此我們將會引入一個新的實體:author。形成了這樣一個包含 title 和 ISBN 連同 author 列表的 book。在這個例子中將會得到一個包含新實體的 JSON 對象,與前面的JSON對象不同,就像下面那樣:

{
  "title": "Java Puzzlers: Traps, Pitfalls, and Corner Cases",
  "isbn": "032133678X",
  "authors": [
    {
      "id": 1,
      "name": "Joshua Bloch"
    },
    {
      "id": 2,
      "name": "Neal Gafter"
    }
  ]
}
注意前一個例子中 authors 只是一個簡單的字符數組:

"authors": [
    "Joshua Bloch",
    "Neal Gafter"
  ]
這個例子中的 authors 是一個 JSON 對象,而不僅僅只是一個基本類型。

{
      "id": 1,
      "name": "Joshua Bloch"
    }
author 的 JSON對象有一個 id 和一個 name字段。下面是 Author 類。

public class Author {
 
  private int id;
  private String name;
 
  // Methods removed for brevity
 
}

這個類非常簡單,由兩個字段組成,並且都是原始類型。Book 類被修改爲使用 Author 類,如下所示:

public class Book {
 
  private Author[] authors;
  private String isbn;
  private String title;
 
  // Methods removed for brevity
 
}

author 字段從一個 integer 數組變成了一個 Author 數組。因此必須修改下 BookSerialiser 類來兼容這一改變,如下:

import java.lang.reflect.Type;
 
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
 
public class BookSerialiser implements JsonSerializer<Book> {
 
  @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", book.getIsbn());
 
    final JsonElement jsonAuthros = context.serialize(book.getAuthors());
    jsonObject.add("authors", jsonAuthros);
 
    return jsonObject;
  }
}
authors 的序列化由 context(作爲 serialize() 方法的一個參數被傳進來,是 JsonSerializationContext 的實例) 來完成。context 將會序列化給出的對象,並返回一個 JsonElement。同時 context 也會嘗試找到一個可以序列化當前對象的序列化器。如果沒有找到,其將會使用默認的序列化器。目前,我們還不會爲 Author 類實現一個序列化器,仍然會使用默認實現作爲替代。

import java.io.IOException;
 
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
 
public class Main {
 
  public static void main(final String[] args) throws IOException {
    // Configure GSON
    final GsonBuilder gsonBuilder = new GsonBuilder();
    gsonBuilder.registerTypeAdapter(Book.class, new BookSerialiser());
    gsonBuilder.setPrettyPrinting();
    final Gson gson = gsonBuilder.create();
 
    final Author joshuaBloch = new Author();
    joshuaBloch.setId(1);
    joshuaBloch.setName("Joshua Bloch");
 
    final Author nealGafter = new Author();
    nealGafter.setId(2);
    nealGafter.setName("Neal Gafter");
 
    final Book javaPuzzlers = new Book();
    javaPuzzlers.setTitle("Java Puzzlers: Traps, Pitfalls, and Corner Cases");
    javaPuzzlers.setIsbn("032133678X");
    javaPuzzlers.setAuthors(new Author[] { joshuaBloch, nealGafter });
 
    final String json = gson.toJson(javaPuzzlers);
    System.out.println(json);
  }
}

上面的例子創建並配置 Gson 使用自定義的 BookSerialiser 進行序列化。我們創建了兩個 author 對象和一個 book 對象,並序列化該 book 對象。這樣將會得到在章節開始時展示的 JSON 對象。

完整起見,下面是一個 Author 類的序列化器:

import java.lang.reflect.Type;
 
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
 
public class AuthorSerialiser implements JsonSerializer<Author> {
 
  @Override
  public JsonElement serialize(final Author author, final Type typeOfSrc, final JsonSerializationContext context) {
    final JsonObject jsonObject = new JsonObject();
    jsonObject.addProperty("id", author.getId());
    jsonObject.addProperty("name", author.getName());
 
    return jsonObject;
  }
}

上面的序列化器並沒有引入什麼新的特性,因此不需要再多做解釋。如果想要使用這個新的序列器,需要將其註冊到 GsonBuilder (Java Doc) 中。

本章對嵌套對象的序列化做了總結。可以將嵌套對象的序列化交給 context 處理,其在序列化時會順帶嘗試找到一個合適的序列化器,並返回相應的 JsonElement。下一章也是最後一章將會講如何處理對象引用的序列化。

對象引用

一個對象對其它對象的引用叫做對象引用,book 和 author 類之間的關係就是這樣。同一個 author 可以有多本 book。例如 author Joshua Bloch(Author at Amazon)不止一本 book。在使用序列化器描述之前,我們將會清除重複的 author。

考慮下面這個例子:

import java.io.IOException;
 
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
 
public class Example1 {
 
  public static void main(final String[] args) throws IOException {
    // Configure GSON
    final GsonBuilder gsonBuilder = new GsonBuilder();
    gsonBuilder.setPrettyPrinting();
    final Gson gson = gsonBuilder.create();
 
    final Author joshuaBloch = new Author();
    joshuaBloch.setId(1);
    joshuaBloch.setName("Joshua Bloch");
 
    final Author nealGafter = new Author();
    nealGafter.setId(2);
    nealGafter.setName("Neal Gafter");
 
    final Book javaPuzzlers = new Book();
    javaPuzzlers.setTitle("Java Puzzlers: Traps, Pitfalls, and Corner Cases");
    javaPuzzlers.setIsbn("032133678X");
    javaPuzzlers.setAuthors(new Author[] { joshuaBloch, nealGafter });
 
    final Book effectiveJava = new Book();
    effectiveJava.setTitle("<span class="wp_keywordlink"><a href="http://www.amazon.com/gp/product/B000WJOUPA/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=B000WJOUPA&linkCode=as2&tag=job0ae-20" title="Effective Java" rel="nofollow" target="_blank" class="external">Effective Java</a></span> (2nd Edition)");
    effectiveJava.setIsbn("0321356683");
    effectiveJava.setAuthors(new Author[] { joshuaBloch });
 
    final Book[] books = new Book[] { javaPuzzlers, effectiveJava };
 
    final String json = gson.toJson(books);
    System.out.println(json);
  }
}
兩個作者和兩本書,其中一個作者在兩本書中都有。注意,有兩個 author 對象而不是三個,代表 Joshua Bloch 的實例被兩個 book 對象共享。最後注意,我們故意沒有使用任何自定義的序列化器。下面是執行上面代碼的結果。

[
  {
    "authors": [
      {
        "id": 1,
        "name": "Joshua Bloch"
      },
      {
        "id": 2,
        "name": "Neal Gafter"
      }
    ],
    "isbn": "032133678X",
    "title": "Java Puzzlers: Traps, Pitfalls, and Corner Cases"
  },
  {
    "authors": [
      {
        "id": 1,
        "name": "Joshua Bloch"
      }
    ],
    "isbn": "0321356683",
    "title": "Effective Java (2nd Edition)"
  }
]
得到了兩個 book 類型的 JSON 和三個 author 類型的 JSON。而對於author:Joshua Bloch 是重複的。

{
       "id": 1,
       "name": "Joshua Bloch"
     }
這會使得 JSON 對象明顯變大,尤其是對更復雜的對象。理想情況下,book 的 JSON 對象應該只包含 author 的 id 而不是整個對象。這類似於關係型數據庫中 book 表有一個外鍵關聯到 author 表。

Book 類將會被修改爲包含一個只返回 author 的 id 的方法,如下:

public class Book {
 
  private Author[] authors;
  private String isbn;
  private String title;
 
  public Author[] getAuthors() {
    return authors;
  }
 
  public int[] getAuthorsIds() {
    final int[] ids = new int[authors.length];
    for (int i = 0; i < ids.length; i++) {
      ids[i] = authors[i].getId();
    }
    return ids;
  }// Other methods removed for brevity
 
}
當序列化 book 時,使用 author 的 id 代替整個 author 對象,如下:

import java.lang.reflect.Type;
 
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonSerializationContext;
import com.google.gson.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", book.getIsbn());
 
    final JsonElement jsonAuthros = context.serialize(book.getAuthorsIds());
    jsonObject.add("authors", jsonAuthros);
 
    return jsonObject;
  }
}
下面是使用這個序列化器得到的結果:

[
  {
    "title": "Java Puzzlers: Traps, Pitfalls, and Corner Cases",
    "isbn": "032133678X",
    "authors": [
      1,
      2
    ]
  },
  {
    "title": "Effective Java (2nd Edition)",
    "isbn": "0321356683",
    "authors": [
      1
    ]
  }
]

現在我們使用 author 的 id 代替了整個 author。這一做法使得其比前一個有更小的空間佔用。對於更大的對象將產生巨大的影響。

這個例子是整篇文章關於序列化的總結。從中我們學會了怎麼使用默認或自定義的序列化選項,將 Java 對象序列化爲 JSON 字符串。













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