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):
其中功能性是難點(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)原理遭垛,知道怎么做即可。