Android中Gson的使用

Android中Gson的使用

1 簡(jiǎn)介

Gson是一個(gè)Java庫(kù),作用是將Java對(duì)象轉(zhuǎn)換成它對(duì)應(yīng)的JSON表示。
開源的JSON解析庫(kù)有好幾個(gè),例如Jackson就是一個(gè)很有名的XML/JSON解析庫(kù)。Gson與這些庫(kù)的不同之處,表現(xiàn)在它的兩個(gè)重要設(shè)計(jì)目標(biāo):

  • 即使你無(wú)法修改源代碼,你也能通過(guò)Gson對(duì)代碼中的類做解析和變換。
  • 充分支持Java泛型。

在學(xué)習(xí)過(guò)程中我們能發(fā)現(xiàn),第一點(diǎn)很好理解,并且也簡(jiǎn)化了對(duì)Gson的使用;而第二點(diǎn)對(duì)泛型的支持,則是掌握Gson框架的一個(gè)難點(diǎn)。

下圖是Gson的設(shè)計(jì)目標(biāo):


image

其中功能性是難點(diǎn),也比較不好理解,先重點(diǎn)掌握Gson的基本使用方式即可。

2 快速上手

Gson的基本使用是非常簡(jiǎn)單的。
首先在Android Studio項(xiàng)目中添加對(duì)Gson庫(kù)的依賴:

compile 'com.google.code.gson:gson:2.6.2'

3 簡(jiǎn)單回顧Json

在正式開始使用Gson之前,先來(lái)簡(jiǎn)單回顧一下JSON的基礎(chǔ)知識(shí)。
JSON是一種輕量級(jí)的數(shù)據(jù)交換格式,它主要有兩種結(jié)構(gòu):
鍵值對(duì)的集合,對(duì)應(yīng)于其它編程語(yǔ)言中的對(duì)象(例如Java)、struct(例如C)、Hash(例如Ruby)、字典(例如Python)等。
無(wú)序的值列表,對(duì)應(yīng)于編程語(yǔ)言中的數(shù)據(jù)結(jié)構(gòu):數(shù)組、矢量、列表等。
注意JSON使用的這兩種數(shù)據(jù)結(jié)構(gòu)的簡(jiǎn)潔性,它們是不同編程語(yǔ)言之間通用的,這賦予了JSON連接不同編程語(yǔ)言的能力。
JSON中主要有5種數(shù)據(jù)形式:

  • number,例如10
  • string,例如“abcd”
  • value,value可以是上面的number和String,也可以是下面的object和array,value的概念賦予了JSON嵌套表示的能力。value還包括三個(gè)特殊值:true,false和null。
  • array,形如 [value, value, ... value] 的列表形式。
  • object,形如 {string:value, string:value, ... string:value} 的鍵值對(duì)形式。

4 使用Gson

Gson庫(kù)的關(guān)鍵類就是Gson,使用起來(lái)很簡(jiǎn)單,直接實(shí)例化 new Gson() 即可。它也可以通過(guò)GsonBuilder 類來(lái)實(shí)例化,進(jìn)行一些初始化設(shè)置,初學(xué)時(shí)無(wú)需深入了解。
Gson實(shí)例是無(wú)狀態(tài)的。所以多個(gè)JSON序列化和反序列操作完全可以重用一個(gè)Gson對(duì)象。

4.1 基本序列化方法

序列化是指將Java對(duì)象轉(zhuǎn)換成JSON字符串,使用Gson.toJson(...)方法來(lái)進(jìn)行序列化操作。舉幾個(gè)基本的序列化例子:

  • number:
Gson gson = new Gson();
gson.toJson(1);            // ==> 1
gson.toJson(new Long(10)); // ==> 10
  • string:
gson.toJson("abcd");       // ==> "abcd"
  • value:
gson.toJson(true);         // ==> true
  • object:
class BagOfPrimitives {
    private int value1 = 1;
    private String value2 = "abc";
    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"}
  • array:
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"]

4.2 基本反序列化方法

反序列化是指將JSON字符串傳換成Java對(duì)象,使用Gson.fromJson(...)方法來(lái)進(jìn)行反序列化操作。反序列化和序列化不同的一點(diǎn)是,你需要告訴Gson目標(biāo)對(duì)象的類型是什么。舉幾個(gè)基本的反序列化例子:

  • number:
int one = gson.fromJson("1", int.class);
Long one = gson.fromJson("1", Long.class);
  • string:
String str = gson.fromJson("\"abc\"", String.class);
  • value:
Boolean f = gson.fromJson("false", Boolean.class);
  • object:
class BagOfPrimitives {
    private int value1 = 1;
    private String value2 = "abc";
    private transient int value3 = 3;
    BagOfPrimitives() {
    // no-args constructor
    }
}
String json = "{"value1":1,"value2":"abc"}";  
BagOfPrimitives obj2 = gson.fromJson(json, BagOfPrimitives.class);
  • array:
int[] ints = {1, 2, 3, 4, 5};
int[] ints2 = gson.fromJson("[1,2,3,4,5]", int[].class); 
// ==> ints2 和 ints 相同。

4.3 序列化和反序列化中的注意點(diǎn)

  • 對(duì)象的字段可以并推薦使用private。
  • 無(wú)需使用注解指定哪些字段被包括在序列化和反序列化操作中。當(dāng)前類,和它所有父類的所有字段都是默認(rèn)包括的。
  • 如果字段使用transient修飾,它被默認(rèn)忽略,不包含在序列化和反序列化操作中。
  • Gson可以正確處理null值。
  • 序列化時(shí),值為null的字段會(huì)被跳過(guò)。
  • 反序列化時(shí),JSON中缺失的項(xiàng),其對(duì)象對(duì)應(yīng)的字段會(huì)被設(shè)為null。
  • 內(nèi)部類、匿名類等隱含的外部類的字段會(huì)被忽略,不包含在序列化和反序列化中。

5 Gson的進(jìn)階使用

5.1 修改字段名

從上面的例子可以看出來(lái),在序列化和反序列化時(shí),字段名直接用來(lái)作為鍵值對(duì)中的key。有時(shí),對(duì)象和它的JSON表示命名并不完全一致,這時(shí)就需要修改字段名,有兩種方法:
使用@SerializeName注解自定義字段名;
使用FieldNamingPolicy來(lái)規(guī)定基本的命名策略;
例如:

private class SomeObject {
  @SerializedName("custom_naming") 
  private final String someField;

  private final String someOtherField;

  public SomeObject(String a, String b) {
    this.someField = a;
    this.someOtherField = b;
  }
}

SomeObject someObject = new SomeObject("first", "second");
Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE).create();
String jsonRepresentation = gson.toJson(someObject);
System.out.println(jsonRepresentation);

結(jié)果將是

{"custom_naming":"first","SomeOtherField":"second"}

5.2 嵌套類(例如內(nèi)部類)的情況

對(duì)于靜態(tài)內(nèi)部類,Gson可以直接處理。
對(duì)于非靜態(tài)內(nèi)部類,Gson不能自動(dòng)反序列化,因?yàn)榧词故欠庆o態(tài)內(nèi)部類的無(wú)參構(gòu)造器,也需要一個(gè)外部類的引用,這在反序列化期間是做不到的。要解決這個(gè)問(wèn)題,你要么將內(nèi)部類聲明為靜態(tài),要么提供一個(gè)自定義的InstanceCreator。例如:

public class A { 
  public String a; 

  class B { 

    public String b; 

    public B() {
    // No args constructor for B
    }
  } 
}

注意:默認(rèn)情況下,B類不能用Gson序列化。

Gson不能講{"b":"abc"}反序列化成B的對(duì)象,除非將B定義成靜態(tài)的。另一個(gè)解決方法是寫一個(gè)自定義的InstanceCreator:

public class InstanceCreatorForB implements InstanceCreator<A.B> {
  private final A a;
  public InstanceCreatorForB(A a)  {
    this.a = a;
  }
  public A.B createInstance(Type type) {
    return a.new B();
  }
}

這是可行的,但不推薦這樣來(lái)用。

5.3 集合的情況

Gson對(duì)集合的處理方式比較丑陋,這是由于Java泛型機(jī)制決定的,沒(méi)有更好的解決辦法:

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

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

// Deserialization
Type collectionType = new TypeToken<Collection<Integer>>(){}.getType();
Collection<Integer> ints2 = gson.fromJson(json, collectionType);
// ==> ints2 和 ints 相同。

如果集合中存儲(chǔ)的是任意類型的對(duì)象,Gson可以序列化它,但不能做反序列化,這是因?yàn)橛脩魺o(wú)法指明集合中每一個(gè)對(duì)象的類型。在反序列化時(shí),集合必須是一個(gè)明確的類型。如果遵循良好的Java編程規(guī)范,這基本不會(huì)造成什么問(wèn)題。

5.4 泛型的處理

泛型的處理和上面集合的處理類似,因?yàn)榧隙x中使用的就是泛型。
當(dāng)你調(diào)用toJson(obj)方法時(shí),Gson使用obj.getClass()來(lái)獲取類屬性信息,來(lái)序列化它。類似的,通常你可以將MyClass.class對(duì)象傳遞給fromJson(json, MyClass.class)方法,來(lái)反序列化。如果對(duì)象不包含泛型,這種方法是可行的,但是,如果對(duì)象是一個(gè)泛型類型,由于Java的類型擦除(Type Erasure)機(jī)制,該泛型的信息會(huì)丟失,例如:

class Foo<T> {
  T value;
}
Gson gson = new Gson();
Foo<Bar> foo = new Foo<Bar>();
gson.toJson(foo); // 可能無(wú)法正確序列化foo.value。

gson.fromJson(json, foo.getClass()); // 不會(huì)將 foo.value 正確反序列化成Bar對(duì)象

要解決這一問(wèn)題,你需要指明你的泛型類型的類型參數(shù)(parameterized type),使用集合的例子中看到過(guò)的TypeToken類:

Type fooType = new TypeToken<Foo<Bar>>() {}.getType();
gson.toJson(foo, fooType);

gson.fromJson(json, fooType);

初步使用Gson時(shí),可以不必深究這一泛型處理的實(shí)現(xiàn)原理,知道怎么做即可。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容