Gson介紹

Gson

目前主流的json解析類庫有jackson,fastjson,gson,gson的serialization deserialization解析功能無疑是最強大的,基本上完美支持複雜對象的json化和反json化,其餘兩個庫在轉換複雜對象時都容易出現問題。若是在不考慮效率的狀況下,我強烈推薦使用gson類庫。java

首先須要添加依賴git

//gradle

dependencies {
    compile 'com.google.code.gson:gson:2.8.2'
}

//maven
<dependencies>
    <!--  Gson: Java to Json conversion -->
    <dependency>
      <groupId>com.google.code.gson</groupId>
      <artifactId>gson</artifactId>
      <version>2.8.2</version>
      <scope>compile</scope>
    </dependency>
</dependencies>

接下來看一些代碼例子,gson設計的很是易用,基本上一看就懂。github

 

轉換基礎數據類型

// 建立gson對象,gson對象是內部無狀態的,因此建立一個能夠屢次使用,能夠想象成一個轉換器
Gson gson = new Gson();
//轉換成json只用直接放入對象就行,gson在內部會提取對象的類型信息
gson.toJson(1);            // ==> 1
gson.toJson("abcd");       // ==> "abcd"
gson.toJson(new Long(10)); // ==> 10
int[] values = { 1 };
gson.toJson(values);       // ==> [1]

// 反json化,此時須要傳入類型參數,由於json中是沒有保存關於java類型的信息的
int one = gson.fromJson("1", int.class);
Integer one = gson.fromJson("1", Integer.class);
Long one = gson.fromJson("1", Long.class);
String str = gson.fromJson("\"abc\"", String.class);

 

轉換對象類型

固然,gson也支持對對象類型的轉換json

class BagOfPrimitives {
  private int value1 = 1;
  private String value2 = "abc";
  //使用transient標識的變量不會被json化 
  private transient int value3 = 3;
  BagOfPrimitives() {
    // no-args constructor
  }
}

// Serialization
BagOfPrimitives obj = new BagOfPrimitives();
Gson gson = new Gson();
String json = gson.toJson(obj);  

// ==> json is {"value1":1,"value2":"abc"}

不過注意你不能json化一個具備循環引用的對象,會致使死循環(簡單來講就是a有一個變量爲b,b也有一個變量爲a,此時不管json化誰都會致使死循環)數組

 

使用gson序列化對象的要點

1.徹底可使用private修飾變量類型,由於gson內部是使用反射來進行json化的,因此private也徹底能夠讀取的到 
2.徹底沒有必要使用任何的annotations 來標註哪一個字段須要被包含(有不少其餘庫是使用annotations 來標記的),gson默認會序列化當前類中的全部字段(包括他的全部父類) 
3.若是一個字段被標記爲transient,他不會被Json化 
4.gson對值爲null的字段有很好的支持maven

 

轉換數組類型

Gson gson = new Gson();
int[] ints = {1, 2, 3, 4, 5};
String[] strings = {"abc", "def", "ghi"};

// Serialization
gson.toJson(ints);     // ==> [1,2,3,4,5]
gson.toJson(strings);  // ==> ["abc", "def", "ghi"]

// Deserialization
int[] ints2 = gson.fromJson("[1,2,3,4,5]", int[].class);

 

集合類型

對集合類型是作了特殊支持的,輸出的效果基本和數組同樣,除了元素以外不會輸出其餘的字段例如 size之類的ide

Gson gson = new Gson();
        Collection<Integer> ints = Arrays.asList(1,2,3,4,5);

        // Serialization
        String json = gson.toJson(ints);  // ==> json is [1,2,3,4,5]

        // Deserialization
        //對於泛型類型,由於直接取得的class並不包括泛型類型,因此須要建立type再傳入
        Type collectionType = new TypeToken<Collection<Integer>>(){}.getType();
        Collection<Integer> ints2 = gson.fromJson(json, collectionType);

 

轉換泛型類型

由於gson內部是使用getClass來獲取類型信息,返回的類型都不包括泛型參數類型,因此直接像以前使用的話,序列化和反序列化都會失敗 
(其實不僅是gson,java的泛型自己就被吐槽好久了,各類毛病)gradle

class Foo<T> {
  T value;
}
Gson gson = new Gson();
Foo<Bar> foo = new Foo<Bar>();
gson.toJson(foo); //不能成功序列化

gson.fromJson(json, foo.getClass()); // 沒法反序列化

若是想要成功序列化帶有泛型類型的對象,須要使用Type對象ui

//注意這裏的TypeToken帶有{},實際上是一個匿名類,由於TypeToken的構造器是非Public的,不能直接構造,我也不太清楚爲何要這樣設計
Type fooType = new TypeToken<Foo<Bar>>() {}.getType();
gson.toJson(foo, fooType);

gson.fromJson(json, fooType);

 

轉換帶有混合類型的集合

有時候你可能會在一個集合中放入不一樣的類型(雖然我我的強烈不推薦這樣寫,由於泛型的做用就是爲了省掉沒必要要的cast,你還放入不一樣類型,這不是自找麻煩嗎?) 
可是若是你真的這樣寫了,gson仍是能夠幫你轉換this

Collection collection = new ArrayList();
collection.add("hello");
collection.add(5);
collection.add(new Event("GREETINGS", "guest"));

class Event {
  private String name;
  private String source;
  private Event(String name, String source) {
    this.name = name;
    this.source = source;
  }
}

你徹底能夠像直接同樣直接序列化這個集合,沒有任何問題。 
可是以前也說過,json沒有保存關於java對象類型的任何信息(做爲一個通用的數據交換格式他這樣作是正確的,不然這段json數據就只有gson才能解析了)。 
以前咱們在反序列化的同時都要手動傳入類型參數,可是做爲一個混合集合,顯然沒有一個所謂的’類型參數’,因此直接反序列化是不可能了。 
若是你想要反序列化這樣的json,有如下幾種選擇。 
1.使用原始的json類庫手動解析 
2.註冊一個collection 的adapter(咱們下一篇會講),自定義解析過程(仍是須要手動解析) 
因此若是想要反序列化這樣的集合,不管怎麼樣都要手寫解析,沒什麼辦法,因此最好仍是不要在泛型集合裏放入不一樣的類型

 

除了默認的序列化和反序列化行爲,gson還容許自定義某些類的序列化和反序列化行爲。自定義行爲通常用於須要使json對象具備和原來類不一樣的表示形式,或者默認行爲會報錯的狀況。主要分爲三個類 
Json Serializers: 自定義某種對象類型的序列化行爲

Json Deserializers: 自定義某種對象類型的反序列化行爲

Instance Creators: 自定義某種對象的建立行爲

此時咱們須要配置gsonbuilder,而後用gsonbuilder來建立gson對象,builder模式你們應該很熟悉了 
代碼以下

GsonBuilder gsonBuilder= new GsonBuilder();
gson.registerTypeAdapter(MyType.class, new MySerializer());
gson.registerTypeAdapter(MyType.class, new MyDeserializer());
gson.registerTypeAdapter(MyType.class, new MyInstanceCreator());
Gson gson=gsonBuilder.create();

爲某類型註冊了自定義的序列化和反序列化行爲以後,在轉換的過程當中一旦遇到該類型的對象,就會調用你註冊的行爲來序列化。 
這些的類的實現也很容易。

//自定義序列化,須要實現JsonSerializer接口
private class DateTimeSerializer implements JsonSerializer<DateTime> {
  public JsonElement serialize(DateTime src, Type typeOfSrc, JsonSerializationContext context) {
    return new JsonPrimitive(src.toString());
  }
}
//自定義序列化,須要實現DateTimeDeserializer 接口
private class DateTimeDeserializer implements JsonDeserializer<DateTime> {
  public DateTime deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
      throws JsonParseException {
    return new DateTime(json.getAsJsonPrimitive().getAsString());
  }
}
//自定義對象建立,須要實現InstanceCreator
private class MoneyInstanceCreator implements InstanceCreator<Money> {
  public Money createInstance(Type type) {
    return new Money("1000000", CurrencyCode.USD);
  }
}

在我我的使用gson的過程當中,我發現有些時候是不得不用自定義序列化和反序列化的,由於在使用默認的行爲的狀況下,對一些複雜對象常常出錯,具體緣由我也不知道爲何,不少時候都是出現了死循環,可能java一些類中自己就存在雙向引用吧,此外還有一些其餘的異常,主要緣由都是java一些類自己的繼承結構太深了。

 

咱們以序列化Image爲例(該類直接序列化會報錯) 
這個代碼是我實際項目中使用的,將javafx的image對象序列化(固然對於圖像對象,最後還要使用壓縮流來減小體積)

public class GsonImage implements JsonSerializer<Image>,JsonDeserializer<Image> {

    @Override
    public Image deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
            throws JsonParseException {

        Gson gson=new Gson();
        byte[] bs=gson.fromJson(json.getAsString(), byte[].class);
        BufferedImage image = null;
        try {
            //從字節數組建立圖片
            image = ImageIO.read(new ByteArrayInputStream(bs));
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        Image image2=SwingFXUtils.toFXImage(image, null);

        return image2;
    }

    @Override
    public JsonElement serialize(Image image, Type typeOfSrc, JsonSerializationContext context) {



        Gson gson=new Gson();
        ByteArrayOutputStream stream=new ByteArrayOutputStream();
        BufferedImage bufferedImage=SwingFXUtils.fromFXImage(image, null);
        try {
            ImageIO.write(bufferedImage, "PNG", stream);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        byte[] imgBytes=stream.toByteArray();
        //實際上序列化的是字節數組
        String string=gson.toJson(imgBytes);

        return new JsonPrimitive(string);
    }

}

除了三種自定義行爲以外,gsonbuilder還提供了一些配置

setPrettyPrinting() //設置輸出格式爲可讀性優先(默認是體積小優先)
serializeNulls() //設置輸出null(默認null會被忽略)
excludeFieldsWithModifiers()//設置不被包括在序列化中的修飾符(默認爲static //transient,可是你能夠覆蓋設置)

關於gson就介紹到這裏

能夠訪問gson的github幫助頁面來獲取更多信息 
https://github.com/google/gson/blob/master/UserGuide.md

相關文章
相關標籤/搜索