為了更好的學習Gson,特將Gson User Guide翻譯如下。由于本人英文水平有限,如有錯誤,還請指正,謝謝!
Gson用戶指南
概述
Gson是這樣一個Java類庫,它可以將Java對象轉換為相應的JSON形式,也可以將JSON字符串轉換為對應的Java對象。Gson是一個開源庫,其地址為:http://code.google.com/p/google-gson。
Gson可以使用任意Java對象,包括哪些預先存在的、不在你的源代碼中的對象(因此,你并不知道對象的屬性)。
Gson的目標
- 提供一種機制,使得將Java對象轉換為JSON或相反如使用toString()以及構造器(工廠方法)一樣簡單。
- 允許預先存在的不可變的對象轉換為JSON或與之相反。
- 允許自定義對象的表現形式
- 支持任意復雜的對象
- 輸出輕量易讀的JSON
Gson的性能和可擴展性
下面給出一些我們在同時運行很多其他任務的桌面環境(雙處理器,8GB內存,64位Ubuntu操作系統)下的測試指標。你可以使用PerformanceTest類重復運行這些測試。
- Strings:反序列化超過25MB的字符串沒有出現任何問題(查看PerformanceTest類中的disabled_testStringDeserializationPerformance 方法)
- 大型集合:
1.序列化一個包含超過140億個對象的集合(查看PerformanceTest類中的disabled_testLargeCollectionSerialization 方法)
2.反序列化一個擁有87000個對象的集合(查看PerformanceTest類中的disabled_testLargeCollectionDeserialization方法) - Gson 1.4版本將字節數組和集合的反序列化限制從80KB提升到了11MB。
注:測試的時候刪除disabled_前綴。這個前綴是我們用來進行Junit測試時組織運行這些測試的。
Gson的用戶群
Gson最一開始是谷歌內部使用的,現在已經應用在了許多項目中了。如今,它已經被許多公共項目和公司所使用。詳情請見這里這里。
使用Gson
使用Gson的首要類是Gson類,你可以僅僅通過new Gson()的方式創建它。你也可以通過GsonBuilder類去創建Gson實例,這個類允許你進行一系列配置,例如版本控制等等。
Gson實例不會保存任何進行Json操作時的狀態。因此,你可以自由的服用相同的Gson對象進行諸多的Json序列化和反序列化操作。
原始類型的例子
序列化
Gson gson = new Gson();
gson.toJson(1); ==> prints 1
gson.toJson("abcd"); ==> prints "abcd"
gson.toJson(new Long(10)); ==> prints 10
int[] values = { 1 };
gson.toJson(values); ==> prints [1]
反序列化
int one = gson.fromJson("1", int.class);
Integer one = gson.fromJson("1", Integer.class);
Long one = gson.fromJson("1", Long.class);
Boolean false = gson.fromJson("false", Boolean.class);
String str = gson.fromJson("\"abc\"", String.class);
String anotherStr = gson.fromJson("[\"abc\"]", String.class);
對象的例子
class BagOfPrimitives {
private int value1 = 1;
private String value2 = "abc";
private transient int value3 = 3;
BagOfPrimitives() {
// no-args constructor
}
}
序列化
BagOfPrimitives obj = new BagOfPrimitives();
Gson gson = new Gson();
String json = gson.toJson(obj);
==> json is {"value1":1,"value2":"abc"}
注意你不能序列化一個具有循環引用從而導致無限遞歸的對象。
反序列化
BagOfPrimitives obj2 = gson.fromJson(json, BagOfPrimitives.class);
==> obj2 is just like obj
更好的序列化需要對象具備的要點
- 最好使用private成員變量
- 沒有必要使用注解指示一個成員變量是否需要序列化或反序列化。所有當前類中的成員變量(包括繼承自所有父類的成員變量)都默認支持序列化和反序列化。
- 以下實現得以正確的操作空對象
1、序列化時,一個空的成員變量將會在輸出中被省去。
2、反序列化時,在JSON字符串中缺失的字段將會在相應的成員變量中變為空。 - 如果一個成員變量由synthetic關鍵字標記,在JSON序列化或者反序列化的過程中將會被忽略。
- 如果成員變量對應的是外部類中的內部類,匿名類,本地類則會被忽略,從而不被序列化或反序列化。
嵌套類(包括內部類)的例子
Gson可以輕易的序列化靜態的嵌套類。
Gson同樣可以反序列化靜態嵌套類。然而,Gson不能自動的反序列化一個純內部類,這是因為在創建這樣的對象時它的無參構造器需要一個指向包裹對象(外部類)的引用,而這在反序列化的時候是不可能的。你可以將這個內部類指定為靜態的或者提供一個自定義的實例構造者以解決這個問題。以下是一個例子:
public class A {
public String a;
class B {
public String b;
public B() {
// No args constructor for B
}
}
}
注:上面的B類默認情況下是不能使用Gson進行序列化的。
Gson不能反序列化{“b”:“abc”}為一個B的實例,因為B類是一個內部類。如果它被定義為static class B,那么Gson就可以對其執行反序列化。另外一個解決辦法是為B提供一個自定義的實例構造者。
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();
}
}
以上的辦法是可能的,但不推薦。
數組的例子
Gson gson = new Gson();
int[] ints = {1, 2, 3, 4, 5};
String[] strings = {"abc", "def", "ghi"};
序列化
gson.toJson(ints); ==> prints [1,2,3,4,5]
gson.toJson(strings); ==> prints ["abc", "def", "ghi"]
反序列化
int[] ints2 = gson.fromJson("[1,2,3,4,5]", int[].class);
==> ints2 will be same as ints
我們同樣支持多為數組,以及任意復雜的元素類型。
集合的例子
Gson gson = new Gson();
Collection<Integer> ints = Lists.immutableList(1,2,3,4,5);
序列化
String json = gson.toJson(ints); ==> json is [1,2,3,4,5]
反序列化
Type collectionType = new TypeToken<Collection<Integer>>(){}.getType();
Collection<Integer> ints2 = gson.fromJson(json, collectionType);
ints2 is same as ints
相當丑陋:注意我們如何定義集合的類型。
不幸的是,我們沒有辦法用Java避免這種尷尬。
集合的限制
- 可以序列化任意對象的集合但不能反序列化之
1、這是因為沒有途徑使得用戶可以去提示該對象的類型。 - 反序列化過程中,集合必須制定特定的泛型
所有這些是有意義的,它使得你在遵循好的Java編碼實踐的過程中很少發生錯誤。
序列化和反序列化泛型
當你調用toJson(obj),Gson會調用obj.getClass()去獲取該類里面的相關字段進行序列化。同樣的,你往往可以傳遞MyClass.class對象到fromJson(json, MyClass.class)方法中。如果對象是非泛型的,這將會工作良好。
然而,如果對象是泛型的,Java類型擦除機制將會導致泛型類型的丟失。以下通過一個例子進行闡釋:
class Foo<T> {
T value;
}
Gson gson = new Gson();
Foo<Bar> foo = new Foo<Bar>();
gson.toJson(foo); // May not serialize foo.value correctly
gson.fromJson(json, foo.getClass()); // Fails to deserialize foo.value as Bar
上面的代碼之所以不能講相應的值轉換成對應的條目類型,這是因為Gson調用list.getClass()去獲取它的類型信息時,返回的是Foo.class的原始類型。這意味著Gson沒有辦法知道這個對象的類型是Foo<Bar>,而不僅僅只是解釋為Foo。
你可以通過為你的泛型指定正確的參數化類型來解決這個問題。這里你需要用到TypeToken類。
Type fooType = new TypeToken<Foo<Bar>>() {}.getType();
gson.toJson(foo, fooType);
gson.fromJson(json, fooType);
這個獲取 footType 的習慣性語法,事實上是因為在定義了一個包含getType()方法的匿名本地內部類,該方法返回完整的參數化類型。
序列化和反序列化任意類型的對象的集合
有時候,你所處理的JSON數組包含混合的類型。例如:
['hello',5,{name:'GREETINGS',source:'guest'}]
相應的集合為:
Collection collection = new ArrayList();
collection.add("hello");
collection.add(5);
collection.add(new Event("GREETINGS", "guest"));
Event類的定義為:
class Event {
private String name;
private String source;
private Event(String name, String source) {
this.name = name;
this.source = source;
}
}
你可以序列化該集合而不需要任何額外的操作:toJson(collection)可以將結果寫入到指定的輸出。然而,不能通過fromJson(json, Collection.class)進行反序列化操作,這是因為Gson無法知道如何映射到指定的輸入類型。Gson要求你在使用fromJson的時候提供集合的泛型版本。因此,你有三個選擇:
Option 1: 你可以通過使用Gson的解析器API(較底層的流解析器或者Dom解析器JsonParser)來解析該數組的元素,然后對數組的每個元素分別調用Gson.fromJson()方法。這是首選的途徑。這個例子 闡述了該怎么做。
Option 2: 為Collection.class注冊一個類型適配器以檢查數組的每個成員并將之映射到相應的對象。這種方式的缺陷是將會導致反序列化其他集合類型時是無效的(也就是不夠通用)。
Option 3: 為MyCollectionMemberType注冊類型適配器并在fromJson中使用Collection<MyCollectionMemberType>。只有當你的數組是頂層元素或者你可以改變Collection<MyCollectionMemberType>集合類型中的成員字段時,這種方式才是有效的。
內置的序列化器和反序列化器
Gson為常用類型定義了一些內置的序列化器和反序列化器,但這些類有時候工作可能并不良好。這里給出一些具體的類:
1、匹配java.net.URL的字符串如“http://code.google.com/p/google-gson/”。
2、匹配java.net.URL的字符串如“/p/google-gson/”
你可以通過查看遠嗎找到這些常用類,比如Joda。
自定義序列化和反序列化機制
有時候,默認的實現并不是你想要的。這在處理類庫時常常發生(例如DateTime)。Gson允許你注冊自己自定義的序列化器和反序列化器。該過程分為兩部分:
Json序列化器:需要為一個對象自定義序列化機制。
Json反序列化器:需要為一個類型自定義反序列化機制。
-
實例構造者:并不需要,如果無參構造器是可用的或者注冊了一個反序列化器。
GsonBuilder gson = new GsonBuilder(); gson.registerTypeAdapter(MyType2.class, new MyTypeAdapter()); gson.registerTypeAdapter(MyType.class, new MySerializer()); gson.registerTypeAdapter(MyType.class, new MyDeserializer()); gson.registerTypeAdapter(MyType.class, new MyInstanceCreator());
registerTypeAdapter會檢查類型適配器是否實現了上面三個接口中的一個以上并且它們都注冊了類型適配器。
寫一個序列化器
這里通過一個例子說明如何為JodaTime DateTime類自定義序列化器。
private class DateTimeSerializer implements JsonSerializer<DateTime> {
public JsonElement serialize(DateTime src, Type typeOfSrc, JsonSerializationContext context) {
return new JsonPrimitive(src.toString());
}
}
當序列化到DateTime對象時,Gson會調用toJson()。
寫一個反序列化器
這里通過一個例子說明如何為JodaTime DateTime類自定義反序列化器。
private class DateTimeDeserializer implements JsonDeserializer<DateTime> {
public DateTime deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
return new DateTime(json.getAsJsonPrimitive().getAsString());
}
}
當需要反序列化一個JSON字符串片段到DateTime對象時,Gson會調用fromJson()。
序列化器和反序列化器的幾個要點
通常,你想要為所有泛型類型注冊僅一個序列化器和反序列化器以處理其相應的唯一的原始類型。
- 例如,假設你有一個叫做“Id”的類需要用Id表述或轉換(i.e. an internal vs. external representation)。
- Id<T>類型將會為所有的泛型使用同一個序列化器
1、基本上輸出id值 - 反序列化機制是類似的但并不完全相同
1、需要調用"new Id(Class<T>, String)"以返回Id<T>的實例。
Gson支持為此注冊一個單一處理器。你也可以為某個特定泛型注冊一個特定的處理器(如Id(RequiresSpecialHanding)就需要特定的處理器)。
toJson和fromJson方法中的類型參數所包含的泛型參數信息幫助你為所有的泛型類型定義一個處理器以匹配相同的原始類型。
寫一個實例構造者
反序列化一個對象時,Gson需要為該類型創建一個默認實例。
可以良好的進行序列化和反序列化的類應該擁有一個無參構造器 —— 無論其是由public還是由private修飾的。
通常來說,實例構造者的使用時機是在你需要處理沒有定義無參構造器的庫類時。
實例構造者的例子
private class MoneyInstanceCreator implements InstanceCreator<Money> {
public Money createInstance(Type type) {
return new Money("1000000", CurrencyCode.USD);
}
}
類型可以是一個相應的泛型
- 對于啟動一個需要特定泛型類型信息的構造器非常有用
- 例如,Id類存儲了創建Id的類。
使用InstanceCreator處理參數化類型
有時候,你試圖去實例化的類型是一個參數化類型。通常,這不會有什么問題,因為這是一個原始類型的實例。例子如下:
class MyList<T> extends ArrayList<T> {
}
class MyListInstanceCreator implements InstanceCreator<MyList<?>> {
@SuppressWarnings("unchecked")
public MyList<?> createInstance(Type type) {
// No need to use a parameterized list since the actual instance will have the raw type anyway.
return new MyList();
}
}
然而,有時候你需要創建的實例是基于實際的參數化類型。在這種情況下,你可以使用傳入createInstance方法中的類型參數。例子如下:
public class Id<T> {
private final Class<T> classOfId;
private final long value;
public Id(Class<T> classOfId, long value) {
this.classOfId = classOfId;
this.value = value;
}
}
class IdInstanceCreator implements InstanceCreator<Id<?>> {
public Id<?> createInstance(Type type) {
Type[] typeParameters = ((ParameterizedType)type).getActualTypeArguments();
Type idType = typeParameters[0]; // Id has only one parameterized type T
return Id.get((Class)idType, 0L);
}
}
在上面的例子中,如果沒有傳入參數化類型的實際類型,那么Id類是無法創建的。我們通過使用傳入的type來解決這個問題。這種情況下,該type是表示Id<Foo>的Java參數化類型的對象,該對象的實際類型綁定到了Id<Foo>。Id類僅僅只有一個參數化類型參數T,因此我們可以使用getActualTypeArgument()返回的第0個參數,這種情況下該參數將會擁有Foo.class。
緊湊和漂亮的JSON格式輸出對比
Gson默認提供的默認JSON輸出是一個緊湊的JSON格式。這意味著輸出的JSON格式中將不會有空格字符。因此,在JSON輸出中無論是字段名和他們的值之間,對象域以及數組所包含的對象之間都不會有空格。同樣,"null"字段將會在JSON輸出中被忽略(注:null值在集合或數組中不會被忽略)。查看Null對象支持部分的相關信息并進行相關配置以使Gson的輸出支持null值。
如果你想要輸出為漂亮的結構,你需要使用GsonBuilder來配置你的Gson實例。JsonFormatter類在我們的公開API中并沒有暴露,因此,客戶端不能為JSON輸出配置默認的打印設置或間隔。現在,我們僅僅提供了一個默認的JsonPrintFormatter類,該類默認:行寬為80個字符,2個字符的首行間距以及4個字符的右間距。
下面的例子展示如何使用默認的JsonPrintFormatter代替JsonCompatFormatter配置一個Gson實例:
Gson gson = new GsonBuilder().setPrettyPrinting().create();
String jsonOutput = gson.toJson(someObject);
Null對象支持
Gson中的默認實現是,所有空對象將會被忽略。這樣允許更加緊湊的輸出格式;然而,當由JSON格式重新轉換為其Java對象時,客戶必須為這些成員定義一個默認值。
下例展示了如何配置Gson實例以達到輸出null的目的:
Gson gson = new GsonBuilder().serializeNulls().create();
注意:當用Gson序列化空值時,將會在JsonElement結構中添加一個JsonNull元素。因此,該對象可以用于自定義的序列化器或反序列化器。
例子如下:
public class Foo {
private final String s;
private final int i;
public Foo() {
this(null, 5);
}
public Foo(String s, int i) {
this.s = s;
this.i = i;
}
}
Gson gson = new GsonBuilder().serializeNulls().create();
Foo foo = new Foo();
String json = gson.toJson(foo);
System.out.println(json);
json = gson.toJson(null);
System.out.println(json);
======== OUTPUT ========
{"s":null,"i":5}
null
版本支持
同一個對象的不同版本中可以通過使用@Since注解得到保留。該注解可以應用于類、成員變量,在未來甚至可以應用在方法上。為了應用這個特性,你必須配置你的Gson實例以使它忽略所有搞過某些版本的成員變量或對象。如果沒有為Gson實例設置版本,那么它將會忽略版本而序列化和反序列化所有域和類。
public class VersionedClass {
@Since(1.1) private final String newerField;
@Since(1.0) private final String newField;
private final String field;
public VersionedClass() {
this.newerField = "newer";
this.newField = "new";
this.field = "old";
}
}
VersionedClass versionedObject = new VersionedClass();
Gson gson = new GsonBuilder().setVersion(1.0).create();
String jsonOutput = gson.toJson(someObject);
System.out.println(jsonOutput);
System.out.println();
gson = new Gson();
jsonOutput = gson.toJson(someObject);
System.out.println(jsonOutput);
======== OUTPUT ========
{"newField":"new","field":"old"}
{"newerField":"newer","newField":"new","field":"old"}
序列化過程中排除某些域
Gson支持大量的機制以排除高層類,成員變量以及成員變量類型。下面的可插拔式機制允許成員變量和類的排除。如果以下的機制不能滿足你的需求,你通常可以使用自定義序列化器和反序列化器。
Java修飾的排除機制
默認情況下,如果你使用transient標記某個成員變量,它將會被排除。同樣,如果一個成員變量被聲明為“static”那么默認情況下它也將被排除。如果你想要包含某些transient修飾的成員變量,你可以按如下做法:
import java.lang.reflect.Modifier;
Gson gson = new GsonBuilder()
.excludeFieldsWithModifiers(Modifier.STATIC)
.create();
注:你可以為“excludeFieldsWithModifiers”方法提供任意數量的Modifier常量。例如:
Gson gson = new GsonBuilder()
.excludeFieldsWithModifiers(Modifier.STATIC, Modifier.TRANSIENT, Modifier.VOLATILE)
.create();
Gson的@Expose注解
該特性提供了一種你可以明確指定那些你不想要執行JSON的序列化和反序列化的成員變量。使用該注解,你必須使用new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create()來創建Gson實例。該Gson實例將會排除該類中所有沒有使用@Expose注解標記的成員變量。
用戶定義的排除策略
如果以上的機制還不能滿足你的需求,那么通常你可以定義你自己的排除策略然后將之插入Gson中。查看ExclusionStrategyJava文檔查看更多信息。
以下例子展示了如何排除一個使用特定注解“@Foo”標記的成員變量,該注解還可以排除高層類型(或者公開的成員變量的類型)的類字符串。
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface Foo {
// Field tag only annotation
}
public class SampleObjectForTest {
@Foo private final int annotatedField;
private final String stringField;
private final long longField;
private final Class<?> clazzField;
public SampleObjectForTest() {
annotatedField = 5;
stringField = "someDefaultValue";
longField = 1234;
}
}
public class MyExclusionStrategy implements ExclusionStrategy {
private final Class<?> typeToSkip;
private MyExclusionStrategy(Class<?> typeToSkip) {
this.typeToSkip = typeToSkip;
}
public boolean shouldSkipClass(Class<?> clazz) {
return (clazz == typeToSkip);
}
public boolean shouldSkipField(FieldAttributes f) {
return f.getAnnotation(Foo.class) != null;
}
}
public static void main(String[] args) {
Gson gson = new GsonBuilder()
.setExclusionStrategies(new MyExclusionStrategy(String.class))
.serializeNulls()
.create();
SampleObjectForTest src = new SampleObjectForTest();
String json = gson.toJson(src);
System.out.println(json);
}
======== OUTPUT ========
{"longField":1234}
JSON域命名支持
Gson支持某些預先定義的成員變量命名策略以將基本的Java成員變量名稱(如以小寫字母開頭的駝峰命名規則---“simpleFieldNameInJava”)轉換為Json域名(如sample_field_name_in_java o 或者SampleFieldNameInJava)。查看FieldNamingPolicy類以獲取關于預先定義命名策略的信息。
同樣Gson有基于注解的策略以允許客戶程序員為每一個域名自定義名稱。注意,基于注解的策略擁有域名名稱的有效性檢查機制,當它檢查到某個提供了該注解的域名為非法時,會拋出“Runtime”異常。
下面的例子展示了如何使用Gson命名機制的特性:
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);
======== OUTPUT ========
{"custom_naming":"first","SomeOtherField":"second"}
如果你需要使用自定義命名機制(查看該論壇),你可以使用@SerializedName注解。
在自定義的序列化器和反序列化器之間分享狀態
有時候你需要在自定義的序列化器或反序列化器之間分享狀態(查看該論壇)。你可以通過下面三個途徑實現:
1、在靜態成員變量中保存分享的狀態。
2、定義序列化器或反序列化器為父類型的內部類,然后使用副類型中的成員變量實例保存分享的狀態。
3、使用Java ThreadLocal
1和2是非線程安全的,3是線程安全的。
流
根據Gson的對象模型和數據綁定方式,你可以使用Gson讀寫流。你同樣可以結合流和對象模型的訪問方式以這兩者之間最好的實踐方法。
設計中的問題
查看Gson設計文檔的相關論壇。它同樣包含了可以將用于Json轉換的其他頗具競爭力的Java類庫。
未來改進
在最后的改進列表中,或者如果你想要建議新的功能,查看項目網站中的Issues模塊。