泛型:
Gson使用指南
- 概覽
- Gson的目標(biāo)
- Gson性能和可擴展性
- Gson用戶
- 使用Gson(這里指的是android)
- 在Gradle/Android中使用Gson
- 例子
- 對象實例
- 對象細節(jié)
- 嵌套類(包括內(nèi)部類)
- 數(shù)組示例
- 集合示例
- 序列化和反序列化泛型類型
- 任意類型對象集合的序列化和反序列化
- 內(nèi)置的序列化器和反序列化器
- 自定義序列化和反序列化
- 編寫序列化程序
- 編寫反序列化程序
- 編寫實例創(chuàng)建器
- 緊湊型 vs JSON的漂亮輸出格式
- 空對象支持
- 版本控制支持
- 從序列化和反序列化中排除字段
- JSON字段命名支持
- 在自定義序列化和反序列化之間共享狀態(tài)
- 流
- 設(shè)計Gson的問題
- Gson的未來改進
概覽
Gson是一個Java庫,可用于將Java對象轉(zhuǎn)換為其JSON表示。還可以用于將JSON字符串轉(zhuǎn)換為等效的Java對象。
Gson可以處理任意Java對象谣光,包括沒有在代碼中預(yù)設(shè)的對象指黎。
Gson的目標(biāo)
- 提供易于使用的機制廓啊,如toString()構(gòu)造函數(shù)(工廠方法)甸祭,將Java轉(zhuǎn)換為JSON呢蔫,反之亦然
- 允許將預(yù)先存在的不可修改對象轉(zhuǎn)換為JSON或從JSON轉(zhuǎn)換
- 允許對象的自定義表示
- 支持任意復(fù)雜的對象
- 生成緊湊且可讀的JSON輸出
Gson性能和可擴展性
Gson用戶
Gson的使用
要使用的主要類是Gson恕曲,可以通過調(diào)用創(chuàng)建的new Gson()以及GsonBuilder類用于創(chuàng)建具有各種設(shè)置(如版本控制等)的Gson實例鹏氧。
在調(diào)用Json操作時,Gson實例不維護任何狀態(tài)佩谣。因此把还,您可以重用相同的Gson對象自由地為多個Json進行序列化和反序列化操作。
在Gradle/Android中使用Gson
dependencies {
implementation 'com.google.code.gson:gson:2.8.5'
}
具體版本請參照網(wǎng)站
https://github.com/google/gson
例子:
// 序列化
Gson gson = new Gson();
gson.toJson(1); // ==> 1
gson.toJson("abcd"); // ==> "abcd"
gson.toJson(new Long(10)); // ==> 10
int[] values = { 1 };
gson.toJson(values); // ==> [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{
// 無參構(gòu)造
}
}
// 序列化
BagOfPrimitives obj = new BagOfPrimitives();
Gson gson = new Gson();
String json = gson.toJson(obj);
// ==> json 內(nèi)容是 {"value1":1,"value2":"abc"}
// 反序列化
BagOfPrimitives obj2 = gson.fromJson(json, BagOfPrimitives.class);
// ==> obj2 是一個像 obj 的對象
對象細節(jié):
- 最好使用private修飾字段
- 無需使用任何注釋來指示要進行序列化和反序列化的字段茸俭。默認情況下當(dāng)前類(包括所有從父類繼承)的所有字段都會被包含
- 如果一個字段被transient修飾了吊履,則在序列化和反序列化的過程會被忽略
- Gson實現(xiàn)會正確處理空值
- 當(dāng)序列化時,一個為空的字段將被忽略
- 當(dāng)反序列化時调鬓,在反序列化時艇炎,JSON中缺少的條目導(dǎo)致將對象中的相應(yīng)字段設(shè)置為其默認值:對象類型為null,數(shù)字類型為零腾窝,布爾值為false缀踪。
- 如果字段是合成的,則會被忽略虹脯,并且不包含在JSON序列化或反序列化中驴娃。
- 內(nèi)部類,匿名類和本地類的字段會被忽略并且不被包含在序列化和反序列化中
嵌套類(包括內(nèi)部類)
Gson能夠地序列化和反序列化靜態(tài)嵌套類归形。但是單純的內(nèi)部類不能被Gson序列化和反序列化,因為再反序列的時候需要一個實例的引用去獲得實際的類型鼻由,你可以通過使用靜態(tài)內(nèi)部類或為其提供自定義InstanceCreator來解決此問題暇榴,如:
public class A {
public String a;
class B {
public String b;
public B() {
// B的無參構(gòu)造
}
}
}
注意:上述的類B不能被Gson序列化厚棵。
可以通過定義B為靜態(tài)內(nèi)部類,來讓Gson實現(xiàn)序列化和反序列化,如:
public class A {
public String a;
static class B {
public String b;
public B() {
// B的無參構(gòu)造
}
}
}
也可以通過一個自定義的creator來解決蔼紧,如:
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();
}
}
上述的方法不被推薦婆硬。
數(shù)組示例
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);
// ==> ints2 will be same as ints
集合示例
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 is same as ints
這種寫法不被推薦,但在JAVA中無法解決這個問題。
集合的限制:
Gson可以序列化任意對象的集合奸例,但不能從中反序列化彬犯,因為用戶無法指示結(jié)果對象的類型。相反查吊,在反序列化時谐区,Collection必須是特定的泛型類型。
序列化和反序列化泛型類型
當(dāng)你調(diào)用toJson(obj),Gson會調(diào)用obj.getClass()來獲取有關(guān)要序列的字段的信息逻卖。同樣宋列,你還可以傳遞Myclass.class參數(shù)給fromJson(json, MyClass.class)。如果對象是非泛型類型评也,則此方法正常炼杖。但是,如果對象是泛型類型盗迟,則由于Java類型擦除而丟失通用類型信息坤邪。如:
class Foo<T> {
T value;
}
Gson gson = new Gson();
Foo<Bar> foo = new Foo<Bar>();
gson.toJson(foo); // 不能被正確的序列化
gson.fromJson(json, foo.getClass()); // 不能被正確的反序列化
上面的例子代碼不能將Bar這個類型正確的反序列化,因為Gson調(diào)用list.getClass()去獲取class的信息,但返回的是Foo.class罚缕。這意味著Gson無法知道這是一個Foo<Bar>類型的對象艇纺,而不僅僅是普通的Foo。您可以通過為泛型類型指定正確的參數(shù)化類型來解決此問題怕磨。您可以使用TypeToken該類來完成此操作喂饥。如:
Type fooType = new TypeToken<Foo<Bar>>() {}.getType();
gson.toJson(foo, fooType);
gson.fromJson(json, fooType);
fooType實際上定義了一個匿名本地內(nèi)部類,其中包含一個getType()返回完全參數(shù)化類型的方法肠鲫。
任意類型對象集合的序列化和反序列化
有時候员帮,你會處理這樣有多種數(shù)據(jù)類型的JSON數(shù)組,如:
['hello',5,{name:'GREETINGS',source:'guest'}]
這相當(dāng)于一個這樣的Collection:
class Event {
private String name;
private String source;
private Event(String name, String source) {
this.name = name;
this.source = source;
}
}
Collection collection = new ArrayList();
collection.add("hello");
collection.add(5);
collection.add(new Event("GREETINGS", "guest"));
你可以直接調(diào)用toJson(collection)即可得到上面的JSON數(shù)組字符串导饲。但是反序列化fromJson(json, Collection.class)則無法成功捞高,因為Gson不知道如何去匹配數(shù)據(jù)類型。Gson需要你去提供集合類型的通用版本個體fromJson().這時候你有三個選擇:
- 使用Gson的解析器API(低級流解析器或DOM解析器JsonParser)來解析數(shù)組元素渣锦,然后Gson.fromJson()在每個數(shù)組元素上使用硝岗。這是首選方法。這是一個演示如何執(zhí)行此操作的示例袋毙。
- 注冊一個類型適配器Collection.class型檀,查看每個數(shù)組成員并將它們映射到適當(dāng)?shù)膶ο蟆_@種方法的缺點是它會影響Gson中其他集合類型的反序列化听盖。
- 注冊一個類型的適配器MyCollectionMemberType胀溺,并使用fromJson()與Collection<MyCollectionMemberType>裂七。
僅當(dāng)數(shù)組顯示為頂級元素或者您可以更改將集合保持為類型的字段類型時,才可用Collection<MyCollectionMemberType>此方法仓坞。
內(nèi)置的序列化和反序列化
Gson有常用類的內(nèi)置序列化和反序列化器背零,如:
JodaTime
自定義序列化和反序列化
有時默認表示不是您想要的。處理一些庫類(如DateTime等)時經(jīng)常會出現(xiàn)這種情況无埃。Gson允許您注冊自己的自定義序列化程序和反序列化器徙瓶。這是通過定義兩部分來完成的:
- Json Serializers:必須,為對象定義自定義序列化
- Json Deserializers:必須,為類型定義自定義反序列化
- Instance Creators: 可選,如果無參構(gòu)造方法或者Json Deserializers被注冊了的話,則不需要
例子:
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
的調(diào)用是檢查是否type adapter實現(xiàn)了其中之一的接口嫉称,并且將所有接口都注冊侦镇。
實現(xiàn)Serializer
private class DateTimeSerializer implements JsonSerializer<DateTime> {
public JsonElement serialize(DateTime src, Type typeOfSrc, JsonSerializationContext context) {
return new JsonPrimitive(src.toString());
}
}
Gson 在序列化期間遇到DateTime對象時調(diào)用serialize()。
實現(xiàn)Deserializers
private class DateTimeDeserializer implements JsonDeserializer<DateTime> {
public DateTime deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
return new DateTime(json.getAsJsonPrimitive().getAsString());
}
}
Gson 在反序列化期間遇到DateTime對象時調(diào)用deserialize澎埠。
自定義序列化和反序列化實例:
// Custom Serialize
public class DogSerializer implements JsonSerializer<dog> {
@Override
public JsonElement serialize(Dog src, Type typeOfSrc, JsonSerializationContext context) {
// This method gets involved whenever the parser encounters the Dog
// object (for which this serializer is registered)
JsonObject object = new JsonObject();
String name = src.getName().replaceAll(" ", "_");
object.addProperty("name", name);
// we create the json object for the dog and send it back to the
// Gson serializer
return object;
}
public static void main(String[] args) {
Animal<Dog> animal = new Animll<Dog>();
Dog dog = new Dog("I am a dog");
animal.setAnimal(dog);
// Create the GsonBuilder and register a serializer for the Dog class.
// Whenever the Dog class is encountered Gson calls the DogSerializer
// we set pretty printing own to format the json
Gson gson = new GsonBuilder().registerTypeAdapter(Dog.class, new DogSerializer()).setPrettyPrinting().create();
// Since Animal contains generic type create the type using TypeToken
// class.
Type animalType = new TypeToken<Animal<Dog>>() {
}.getType();
System.out.println(gson.toJson(animal, animalType));
}
}
// The Animal class
public class Animal<t> {
public T animal;
public void setAnimal(T animal) {
this.animal = animal;
}
public T get() {
return animal;
}
}
// The Dog class
public class Dog {
private String name;
public Dog(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
// Custom Deserialize
public class DogDeserialiser implements JsonDeserializer<Dog> {
@Override
public Dog deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
String name = json.getAsJsonObject().get("name").getAsString();
name = name.replace(" ", "_");
Dog dog = new Dog(name);
return dog;
}
public static void main(String[] args) {
String json = "{\"animal\":{\"name\":\"I am a dog\"}}";
Gson gson = new GsonBuilder().registerTypeAdapter(Dog.class, new DogDeserialiser()).create();
Type animalType = new TypeToken<Animal<Dog>>() {
}.getType();
Animal<Dog> animal = gson.fromJson(json, animalType);
System.out.println(animal.get().getName());
}
}
編寫實例創(chuàng)建器
當(dāng)進行對象的反序列化時,Gson創(chuàng)建需要一個類的默認實例虽缕。一個編寫優(yōu)美的類意味著擁有序列化和反序列化所需要的無參構(gòu)造方法(可以是public也可以是private)。
通常如果一個類沒有無參構(gòu)造方法就可以使用實例創(chuàng)建器蒲稳。如:
private class MoneyInstanceCreator implements InstanceCreator<Money> {
public Money createInstance(Type type) {
return new Money("1000000", CurrencyCode.USD);
}
}
這里的類型可以是對應(yīng)的泛型氮趋。
有時,您嘗試實例化的類型是參數(shù)化類型江耀。通常剩胁,這不是問題,因為實際的實例是原始類型祥国。這是一個例子:
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();
}
}
但是昵观,有時您需要根據(jù)實際參數(shù)化類型創(chuàng)建實例。在這種情況下舌稀,您可以使用傳遞給createInstance方法的type參數(shù)啊犬。這是一個例子:
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);
}
}
在上面的示例中,如果沒有實際傳入?yún)?shù)化類型的實際類型壁查,則無法創(chuàng)建Id類的實例吓坚。我們通過使用傳遞的方法參數(shù)type來解決這個問題频丘。在這種情況下,type對象是Java參數(shù)化類型表示Id<Foo>實際實例應(yīng)綁定到的位置Id<Foo>。由于Id類只有一個參數(shù)化類型參數(shù)强衡,T我們使用返回的類型數(shù)組的第0個元素宽堆,在這種情況下getActualTypeArgument()它將保存Foo.class畜普。
緊湊型 vs JSON的漂亮輸出格式
Gson提供的默認JSON輸出是緊湊的JSON格式蛀序。這意味著輸出JSON結(jié)構(gòu)中不會有任何空格。因此挂捻,JSON輸出中的字段名稱及其值碉纺,對象字段和數(shù)組內(nèi)的對象之間不會有空格。同樣,輸出中將忽略“null”字段骨田。
如果要使用“Pretty Print”功能唬涧,則必須Gson使用“ 配置”來配置實例GsonBuilder。Gson沒有通過的公共API公開JsonFormatter盛撑,所以客戶端無法配置默認print settings/margins的JSON輸出。目前捧搞,我們只提供JsonPrintFormatter默認行長度為80個字符抵卫,2個字符縮進和4個字符右邊距。
以下是一個示例胎撇,說明如何配置Gson實例以使用默認值JsonPrintFormatter而不是JsonCompactFormatter:
Gson gson = new GsonBuilder().setPrettyPrinting().create();
String jsonOutput = gson.toJson(someObject);
Null 對象支持
Gson的默認實現(xiàn)中null值是被忽略的介粘。然而,當(dāng)你在使用Gson時想為這里字段添加一個默認值時晚树,你可以這樣配置Gson姻采,如:
Gson gson = new GsonBuilder().serializeNulls().create();
注意:這樣配置后,當(dāng)Gson在序列化null時爵憎,會在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);
輸出為:
{"s":null,"i":5}
null
版本支持
使用@Since注釋可以維護同一對象的多個版本刑棵。此批注可用于類,字段以及將來的發(fā)行版中的方法愚铡。要利用此功能蛉签,必須將Gson實例配置為忽略任何大于某個版本號的字段/對象。如果沒有在Gson實例上設(shè)置任何版本沥寥,則無論版本如何碍舍,它都將序列化和反序列化所有字段和類。如:
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(versionedObject);
System.out.println(jsonOutput);
System.out.println();
gson = new Gson();
jsonOutput = gson.toJson(versionedObject);
System.out.println(jsonOutput);
輸出為:
{"newField":"new","field":"old"}
{"newerField":"newer","newField":"new","field":"old"}
從序列化和反序列化中排除字段
Gson提供大量的排除頂級類邑雅,字段和字段類型的機制片橡。下面是靈活的機制,允許字段和類排除蒂阱。如果以下機制都不能滿足您的需求锻全,那么您始終可以使用自定義序列化程序和反序列化程序。
- Java修飾符排除
默認情況下录煤,如果將字段標(biāo)記為transient鳄厌,則將排除該字段。同樣妈踊,如果某個字段被標(biāo)記為static默認情況下將被排除了嚎。如果要包含一些transient 字段,則可以執(zhí)行以下操作:
import java.lang.reflect.Modifier;
Gson gson = new GsonBuilder()
.excludeFieldsWithModifiers(Modifier.STATIC)
.create();
注意:您可以為excludeFieldsWithModifiers方法提供任意數(shù)量的Modifier常量。如:
Gson gson = new GsonBuilder()
.excludeFieldsWithModifiers(Modifier.STATIC, Modifier.TRANSIENT, Modifier.VOLATILE)
.create();
- Gson的@Expose
可以使用@Expose注解去排除你不想被Gson序列化和反序列化的字段歪泳。但Gson實例必須通過new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create().
來創(chuàng)建萝勤。這樣Gson就會排除掉注解注釋的字段。 - 如果以上的機制都不適合你呐伞,可以編寫你自己的排除策略敌卓,并加入到Gson中。參考ExclusionStrategy文檔伶氢。以下為例子:
@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);
}
輸出為:
{"longField":1234}
JSON字段命名支持
Gson支持一些預(yù)定義的字段命名策略趟径,以將標(biāo)準(zhǔn)Java字段名稱(即以小寫字母開頭的駝峰名稱sampleFieldNameInJava)轉(zhuǎn)換為Json字段名稱(即sample_field_name_in_java或SampleFieldNameInJava)。有關(guān)預(yù)定義命名策略的信息癣防,請參閱FieldNamingPolicy類蜗巧。如:
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);
輸出為:
{"custom_naming":"first","SomeOtherField":"second"}
自定義序列化和反序列化共享狀態(tài)
有時您需要在自定義序列化器/反序列化器之間共享狀態(tài))±俣ⅲ可以使用以下三種策略來完成此任務(wù):
- 在靜態(tài)字段中存儲共享狀態(tài)
- 將序列化器/反序列化器聲明為父類型的內(nèi)部類幕屹,并使用父類型的實例字段來存儲共享狀態(tài)
- 使用Java ThreadLocal
1和2不是線程安全選項,但3是级遭。
流
除了Gson的對象模型和數(shù)據(jù)綁定之外望拖,您還可以使用Gson讀取和寫入流。您還可以組合流和對象模型訪問挫鸽,以獲得兩種方法中的最佳方法靠娱。