轉自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 四種具體實現類型:
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() 方法的一個參數被傳進來,是 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 字符串。