最近在研究Retrofit中使用的Gson的時候,發現對Gson的一些深層次的概念和使用比較模糊,因此這裏作一個知識點的概括整理。java
Gson(又稱Google Gson)是Google公司發佈的一個開放源代碼的Java庫,主要用途爲序列化Java對象爲JSON字符串,或反序列化JSON字符串成Java對象。而JSON(JavaScript Object Notation) 是一種輕量級的數據交換格式,易於人閱讀和編寫,同時也易於機器解析和生成,普遍應用於各類數據的交互中,尤爲是服務器與客戶端的交互。git
JsonElement
有下面這四種類型:提供一種像toString()和構造方法的很簡單的機制,來實現Java 對象和Json之間的互相轉換。github
容許已經存在的沒法改變的對象,轉換成Json,或者Json轉換成已存在的對象。typescript
容許自定義對象的表現形式json
支持任意的複雜對象數組
可以生成可壓縮和可讀的Json的字符串輸出。服務器
1 推薦把成員變量都聲明稱private的app
2 沒有必要用註解(@Expose 註解)指明某個字段是否會被序列化或者反序列化,全部包含在當前類(包括父類)中的字段都應該默認被序列化或者反序列化ide
3 若是某個字段被 transient 這個Java關鍵詞修飾,就不會被序列化或者反序列化性能
4 下面的實現方式可以正確的處理null
1)當序列化的時候,若是對象的某個字段爲null,是不會輸出到Json字符串中的。
2)當反序列化的時候,某個字段在Json字符串中找不到對應的值,就會被賦值爲null
5 若是一個字段是 synthetic
的,他會被忽視,也便是不該該被序列化或者反序列化
6 內部類(或者anonymous class(匿名類),或者local class(局部類,能夠理解爲在方法內部聲明的類))的某個字段和外部類的某個字段同樣的話,就會被忽視,不會被序列化或者反序列化
該註解能指定該字段在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字段中。
該註解可以指定該字段是否可以序列化或者反序列化,默認的是都支持(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();
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);
因爲shirtNumber
和country
做用版本分別是1.2以後,和0.9以前,因此在這裏都不會獲得序列化,因此輸出結果是:
Serialised (version 1.0) {"name":"Albert Attard","teamName":"Zejtun Corinthians"}
英文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-10
和isbn-13
, 咱們怎麼把字段數據isbn10
和isbn13
轉化爲JSON數據須要的isbn-10
和isbn-13
,Gson固然爲咱們提供了對應的解決方案
採用上面提到的@SerializedName
註解。
public class Book { private String[] authors; @SerializedName("isbn-10") private String isbn10; @SerializedName("isbn-13") private String isbn13; private String title; //爲了代碼簡潔,這裏移除getter和setter方法等 }
利用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); }
final JsonObject jsonObject = new 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 對象進行格式化
英文parse和deserialise對應反序列化,這是一個字符串轉換成Java對象的過程。
咱們一樣採用上面一小節的代碼片斷,只不過如今咱們須要作的是將:
{
"title": "Java Puzzlers: Traps, Pitfalls, and Corner Cases", "isbn-10": "032133678X", "isbn-13": "978-0321336781", "authors": [ "Joshua Bloch", "Neal Gafter" ] }
轉化爲對應的Book實體類,
利用@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
字段就能解決這個問題,所有轉化爲咱們實體類中的圖片字段。
咱們在序列化的時候使用的是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對象。因此首先咱們經過final JsonObject jsonObject = json.getAsJsonObject();
將咱們的JsonElement轉化爲JsonObjectjsonObject.get("xxx").getAsString()
的形式獲取相應String的值jsonObject.get("xx").getAsJsonArray();
獲取相應的json數組,並遍歷出其中的相應字段值關於從本地流中讀取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); }
翻譯原文,根據原文作出了較大改動。
1 SIMPLE GSON EXAMPLE
2 GSON DESERIALISER EXAMPLE
3 GSON ANNOTATIONS EXAMPLE
4 GSON SERIALISER EXAMPLE
5 GSON TYPEADAPTER EXAMPLE
6 GSON TYPEADAPTER EXAMPLE SERIALISE LARGE OBJECTS
另附: 你真的會用Gson嗎?Gson使用指南(一)系列文章
更多及時技術資訊,歡迎關注個人微博 :Anthony
本篇文章是基於Gson官方使用指導(Gson User Guide)以及Gson解析的優秀外文(來自http://www.javacreed.com/ )作出的一個翻譯和概括。
博客原連接:
Gson全解析(上)-Gson基礎
Gson全解析(中)-TypeAdapter的使用
Gson全解析(下)-Gson性能分析
from: http://www.jianshu.com/p/fc5c9cdf3aab