Android中Gson的使用

Android中Gson的使用

1 簡介

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

  • 即使你無法修改源代碼,你也能通過Gson對代碼中的類做解析和變換寡润。
  • 充分支持Java泛型捆憎。

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

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


image

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

2 快速上手

Gson的基本使用是非常簡單的诡宗。
首先在Android Studio項(xiàng)目中添加對Gson庫的依賴:

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

3 簡單回顧Json

在正式開始使用Gson之前,先來簡單回顧一下JSON的基礎(chǔ)知識(shí)击儡。
JSON是一種輕量級的數(shù)據(jù)交換格式塔沃,它主要有兩種結(jié)構(gòu):
鍵值對的集合,對應(yīng)于其它編程語言中的對象(例如Java)阳谍、struct(例如C)蛀柴、Hash(例如Ruby)、字典(例如Python)等矫夯。
無序的值列表鸽疾,對應(yīng)于編程語言中的數(shù)據(jù)結(jié)構(gòu):數(shù)組、矢量训貌、列表等制肮。
注意JSON使用的這兩種數(shù)據(jù)結(jié)構(gòu)的簡潔性,它們是不同編程語言之間通用的,這賦予了JSON連接不同編程語言的能力弄企。
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} 的鍵值對形式送悔。

4 使用Gson

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

4.1 基本序列化方法

序列化是指將Java對象轉(zhuǎn)換成JSON字符串祠丝,使用Gson.toJson(...)方法來進(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對象除嘹,使用Gson.fromJson(...)方法來進(jìn)行反序列化操作写半。反序列化和序列化不同的一點(diǎn)是,你需要告訴Gson目標(biāo)對象的類型是什么尉咕。舉幾個(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)

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

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

5.1 修改字段名

從上面的例子可以看出來,在序列化和反序列化時(shí)祭务,字段名直接用來作為鍵值對中的key内狗。有時(shí),對象和它的JSON表示命名并不完全一致义锥,這時(shí)就需要修改字段名柳沙,有兩種方法:
使用@SerializeName注解自定義字段名;
使用FieldNamingPolicy來規(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)部類)的情況

對于靜態(tài)內(nèi)部類赂鲤,Gson可以直接處理。
對于非靜態(tài)內(nèi)部類柱恤,Gson不能自動(dòng)反序列化蛤袒,因?yàn)榧词故欠庆o態(tài)內(nèi)部類的無參構(gòu)造器,也需要一個(gè)外部類的引用膨更,這在反序列化期間是做不到的妙真。要解決這個(gè)問題,你要么將內(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的對象锈候,除非將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();
  }
}

這是可行的敞贡,但不推薦這樣來用泵琳。

5.3 集合的情況

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

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ǔ)的是任意類型的對象,Gson可以序列化它蛔垢,但不能做反序列化击孩,這是因?yàn)橛脩魺o法指明集合中每一個(gè)對象的類型。在反序列化時(shí)鹏漆,集合必須是一個(gè)明確的類型巩梢。如果遵循良好的Java編程規(guī)范创泄,這基本不會(huì)造成什么問題。

5.4 泛型的處理

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

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

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

要解決這一問題,你需要指明你的泛型類型的類型參數(shù)(parameterized type),使用集合的例子中看到過的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)容合作請聯(lián)系作者
  • 序言:七十年代末节槐,一起剝皮案震驚了整個(gè)濱河市搀庶,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌铜异,老刑警劉巖哥倔,帶你破解...
    沈念sama閱讀 211,265評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異揍庄,居然都是意外死亡咆蒿,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評論 2 385
  • 文/潘曉璐 我一進(jìn)店門蚂子,熙熙樓的掌柜王于貴愁眉苦臉地迎上來沃测,“玉大人,你說我怎么就攤上這事食茎⊙客唬” “怎么了?”我有些...
    開封第一講書人閱讀 156,852評論 0 347
  • 文/不壞的土叔 我叫張陵董瞻,是天一觀的道長寞蚌。 經(jīng)常有香客問我田巴,道長,這世上最難降的妖魔是什么挟秤? 我笑而不...
    開封第一講書人閱讀 56,408評論 1 283
  • 正文 為了忘掉前任壹哺,我火速辦了婚禮,結(jié)果婚禮上艘刚,老公的妹妹穿的比我還像新娘管宵。我一直安慰自己,他們只是感情好攀甚,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,445評論 5 384
  • 文/花漫 我一把揭開白布箩朴。 她就那樣靜靜地躺著,像睡著了一般秋度。 火紅的嫁衣襯著肌膚如雪炸庞。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,772評論 1 290
  • 那天荚斯,我揣著相機(jī)與錄音埠居,去河邊找鬼。 笑死事期,一個(gè)胖子當(dāng)著我的面吹牛滥壕,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播兽泣,決...
    沈念sama閱讀 38,921評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼绎橘,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了唠倦?” 一聲冷哼從身側(cè)響起称鳞,我...
    開封第一講書人閱讀 37,688評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎牵敷,沒想到半個(gè)月后胡岔,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,130評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡枷餐,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,467評論 2 325
  • 正文 我和宋清朗相戀三年靶瘸,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片毛肋。...
    茶點(diǎn)故事閱讀 38,617評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡怨咪,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出润匙,到底是詐尸還是另有隱情诗眨,我是刑警寧澤,帶...
    沈念sama閱讀 34,276評論 4 329
  • 正文 年R本政府宣布孕讳,位于F島的核電站匠楚,受9級特大地震影響巍膘,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜芋簿,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,882評論 3 312
  • 文/蒙蒙 一峡懈、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧与斤,春花似錦肪康、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至食寡,卻和暖如春雾狈,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背冻河。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評論 1 265
  • 我被黑心中介騙來泰國打工箍邮, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留茉帅,地道東北人叨叙。 一個(gè)月前我還...
    沈念sama閱讀 46,315評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像堪澎,于是被迫代替她去往敵國和親擂错。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,486評論 2 348