Gson用户使用文档

1.概述
2.Gson的目标
3.Gson的性能和扩展性
4.Gson的使用者
5.如何使用Gson

  • 通过Maven来使用Gson
  • 基本举例
  • 对象举例
  • 使用对象的好处
  • 嵌套类(包括内部类)
  • 数组举例
  • 集合举例
  • 集合的局限性
  • 泛型的序列化和反序列化
  • 任意类型集合的序列化和反序列化
  • 内置的序列化器和反序列化器
  • 定制的序列化和反序列化
  • 写一个序列化解释器
  • 写一个反序列化解释器
  • 写一个实例化工厂(Writing a Instance Creator)
  • 一个参数化类型的实例工厂(InstanceCreator for a Parameterized Type)
  • 简洁和美观的Json格式化输出
  • 支持Null对象
  • 支持版本化
  • 忽略序列化和反序列化中的字段
  • Java修饰符忽略
  • Gson's @Expose
  • 自定义忽略策略
  • JSON字段取名帮助
  • 通过经典的序列化器和反序列化器分享状态

  • 6.在设计Gson中一些问题
    7.未来Gson需要加强的方面

概述


Gson是一个Java库,它不仅可以把Java对象转化为Json格式,它也能将一段Json格式的字符串转化为相对于的Java对象。
Gson适用于所有Java对象,即使是那些你不知道源代码的对象。
Gson的目标


  • 提供简单易用的方法比如 toString() ,构造方法来转化JAVA为JSON以及反转化。
  • 提供已存在的不可修改对象转化为JSON以及反转化。
  • 提供对象的惯例表示
  • 支持任意复杂对象
  • 生成健壮 可读的JSON输出

Gson的性能和扩展性


这里提供我们跑测试的电脑配置(dual opteron, 8GB RAM, 64-bit Ubuntu),你可以通过使用PerformanceTest类来运行测试

  • 能正常反序列化25MB大小的字符串(参照 PerformanceTest中的disabled_testStringDeserializationPerformance)
  • 超大集合:
  • 能正常序列化一百四十万对象(参见PerformanceTest中的disabled_testLargeCollectionSerialization)
  • 能正常反序列化87000个对象的集合(参见PerformanceTest中的disabled_testLargeCollectionDeserialization)
  • Gson 1.4 将反序列的字节数组和集合限制从80KB提高到11MB。
    注意:删除 disabled_前缀 来运行这些TEST,我们使用这个前缀来阻止运行这些测试样例当我们运行Junit的测试样例。

Gson的使用者


Gson最初创建是为了Google内部大量的项目所使用,它现在被很多的开源项目和公司使用。

使用Gson


你可以使用 new Gson()方法 作为使用Gson的开始。 还有一个类GsonBuilder来创建一个Gson实例, 通过这个类你可以设置各种参数包括版本控制等等...

当你调用Json操作时,Gson实例不会维护任何状态, 所以你可以所以复用相同的对象来重复Json的序列化和反序列操作。

通过Maven来使用Gson


通过Maven2/3来使用Gson, 添加下面的依赖来在Maven使用Gson
<dependencies> <dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> <version>2.5</version> <scope>compile</scope> </dependency> </dependencies>
现在你的Maven项目就可以使用Gson

基础举例


// SerializationGson gson = new Gson();gson.toJson(1);// ==> 1 gson.toJson("abcd"); // ==> "abcd" gson.toJson(new Long(10)); // ==> 10 int[] values = { 1 }; gson.toJson(values); // ==> [1] // Deserialization 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 } } // Serialization BagOfPrimitives obj = new BagOfPrimitives(); Gson gson = new Gson(); String json = gson.toJson(obj); // ==> json is {"value1":1,"value2":"abc"}
注意 不要序列对象中使用循环引用,因为它将会导致无限递归
// DeserializationBagOfPrimitives obj2 = gson.fromJson(json, BagOfPrimitives.class);// ==> obj2 is just like obj

使用对象时的一些细节

  • 最好使用private修饰符
  • 不需要通过注释来表明哪些字段需要被序列化和反序列化,默认当前类的所有字段都会被包括
  • 如果一个字段被表示为 transient(默认) 它将会被忽略 不会被包含在Json序列化或者反序列化中
  • nulls会被正确处理
  • 当序列化时 一个Null字段会在输出时被跳过
  • 当反序列化时 Json中一个缺失的实体会自动设置相对应的字段为null
  • 如果一个字段是合成的,它会被直接忽略 也不会被包含在JSON的序列化和反序列化中。
  • 在内部类 匿名类 本地类中 相对应外部类的字段会被忽略,也不会被包含在JSON的序列化和反序列化中。

嵌套类(包括内部类)

Gson可以非常轻松的序列化静态内部类
Gson也可以反序列化静态类,Gson不能自动反序列化完全内部类(pure inner classes) 因为他们无参构造函数需要一个内部对象的引用,然而这个引用在反序列化的时候是不可用的,你可以这样处理这个问题,要么使这个内部类静态化,或者为他提供一个静态工厂方法(instanceCreator),举个例子:
public class A { public String a; class B { public String b; public B() { // No args constructor for B } } }
注意 上面的B类默认是不能被Gson序列化
Gson 不能反序列化{"a":"abc"} 进B实例 因为B类是一个内部类 如果它被定义为静态类ClassB 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"}; // 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); // SerializationString 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中没有办法获取。

集合的局限性

  • 可以序列化集合的任意对象 但是无法从中反序列化
  • 因为使用者根本没有办法声明一个结果对象的类型
  • 当序列化的时候,集合必须是一个特定泛型
    当遵循好的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
上面的代码不能解析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);
我们通常获取fooType 的方式是定义一个匿名内部类,其中包含一个getType方法返回一个全部的参数类型。

任意类型集合的序列化和反序列化


有时 你处理的是JSON数组包含了混合类型 例如 ['hello',5, {name:'GREETINGS',source:'guest'}]
集合实现这种JSON格式是通过:
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; } }
你不需要做额外的操作 当用Gson的toJson(collection)来序列化集合 获得想要的输出结果

然而 使用fromJson(json,Collection.class)反序列化却失效, 因为Gson没办法知道如何将结果映射到类型上,Gson要求你提供一个泛型版本的集合类型在fromJson(), 所以你有三个选择:

  1. 使用 Gson's 解析API(低版本的流解析或者DOM解析) 来解析数组元素 然后使用Gson.fromJson()在对应的数组元素上,这是一个比较好的解决方案,通过一个例子来说明如何使用。
  2. 为Collection.class注册一个类型adapter,来把每一个数组成员都映射到对应的对象,这个方法不好之处在于它会混淆其他集合类型的反序列化
  3. 为MyCollectionMemberType注册一个类型adapter 通过
    Collection<MyCollectionMemberType>来使用fromjson()
    这个方法仅用于数组以顶层元素出现 或者 你可以通过集合的类型Collection<MyCollectionMemberType>来改变字段的类型
    .

内置的序列化器和反序列化器

Gson拥有内置的序列化器和反序列化器,为那些默认显示可能不合适的常用类,下面是这些类的清单:

  1. java.net.URL to match it with strings like "https://github.com/google/gson/"
  2. java.net.URI to match it with strings like "/google/gson/"
    你也可以找一些常用类的源代码 比如JodaTime

定制的序列化和反序列化

有时 默认的表示可能不是你想要的,这常常发生当处理库类的时候(DateTime, etc)Gson允许你来注册你直接的常用的序列化和反序列化, 通常通过定义这两部分来完成。

  • Json Serialiers: 需要为对象定义序列化
  • Json Deserializers: 为需要的类型定义反序列化
  • InstanceCreator: 一般不需要 如果无参构造器可以用或者反序列化以及被注册

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());

写一个序列化器


如下是为 JodaTime DateTime class写一个定制的序列化器
private class DateTimeSerializer implements JsonSerializer<DateTime> { public JsonElement serialize(DateTime src, Type typeOfSrc, JsonSerializationContext context) { return new JsonPrimitive(src.toString()); } }
Gson调用serialize方法当有DateTime对象需要序列化

写一个反序列化器

如下是为 JodaTime DateTime class写一个定制的反序列化器
private class DateTimeDeserializer implements JsonDeserializer<DateTime> { public DateTime deserialize(JsonElement json, Type typeOfT,JsonDeserializationContext context) throws JsonParseException { return new DateTime(json.getAsJsonPrimitive().getAsString()); } }
Gson调用deserialize 当它需要反序列化JSON字符串碎片进DateTime对象

序列化器和反序列化器需要注意事项

通常你要注册单个处理器来为所有反省类型对应一个未处理类型

  • 例如 假设你有一个ID类来表示或者转化ID(比如 外部对应内部表示)
  • Id<T> 类型有相同的序列化为所有泛型类型
  • 反序列化非常相似但是不完全相同
  • 需要调用new Id(Class<T>,String)来返回Id<T>的实例

编写静态工厂方法

当反序列化一个对象时,Gson需要创建以个默认类的实例,一个好的类意味着序列化和反序列化都应该有一个无参构造器

  • public private都行
    通常 静态工厂方法在你处理库类时却没有定义一个无参构造器
    private class MoneyInstanceCreator implements InstanceCreator<Money> { public Money createInstance(Type type) { return new Money("1000000", CurrencyCode.USD); } }
    类型需要是对应的泛型类型
  • 当构造器需要制定的泛型类型信息非常有用
  • 举例 如果Id类能存储那些Id被创建的类

参数化类型的静态工厂方法


有时候 你需要实例化的类型是参数化类型,一般,这不是一个问题 应为实际创建的实例是raw类型,如下是一个例子
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来解决这个问题,类型对象在这个例子中是 Id<Foo>的Java参数化类型的表示,真正的实例应该被绑定到Id<Foo> 因为Id类仅有一个参数化类型的参数 T 我们使用由持有Foo.class的getActualTypeArgument()零元素的类型数组。

简练 优雅打印JSON输出格式


默认JSON输出是由Gson提供的简练JSON格式,这意味着JSON输出格式是没有任何空格,因此 在名 、值、对象数组、对象是没有任何空格的。与此同时null字段也会在输出时被忽略(注意: null值还是会存在集合/数组对象中) 在[Null Object Support] 中有更多关于如何配置Gson来输出所有的Null值

如果你喜欢使用优雅的打印功能,你就一定通过使用GsonBuilder要配置你的Gson实例
JsonFormatter方法是不会通过我们的公有api暴露出去的,所以客户端是无法通过配置默认的答应设置/间距来输出格式化的JSON 现在,我们仅仅提供一个默认的JsonPrintFormatter方法 这个方法默认一场的长度是80字节 2字节缩进 4字节右边距

下面通过一个例子展示如何配置Gson实例,使用默认的JsonPrintFormatter
而不是JsonCompactFormatter
Gson gson = new GsonBuilder().setPrettyPrinting().create(); String jsonOutput = gson.toJson(someObject);

Null对象支持


对于null对象 Gson的默认处理方式是直接忽略 这将提供更简练的输出格式,然而 客户端必须提供一个默认值当JSON格式需要转会对应的JAVA对象中是
你可以配置Gson实例来输出null:
Gson gson = new GsonBuilder().serializeNulls().create();
例子如下:
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注解来维持相同的对象的不同版本,这个注释可以被用来类、域、在未来的版本也会支持 Methods ,为了提高这个功能的影响力,你必须配置你的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);
输出结果
`
{"newField":"new","field":"old"}

{"newerField":"newer","newField":"new","field":"old"}
`

忽略序列化和反序列化中的字段


Java Modifier Exclusion

默认 你可以表示一个字段为transient 它就会被忽略, 同样一个字段被表示为static也会被忽略,如果你想加入一些transicent字段 你可以按下面来操作:
import java.lang.reflect.Modifier;Gson gson = new GsonBuilder() .excludeFieldsWithModifiers(Modifier.STATIC) .create();
注意 你可以使用任意多的修饰符常量给excludeFieldsWithModifiers方法 例如

Gson gson = new GsonBuilder() .excludeFieldsWithModifiers(Modifier.STATIC, Modifier.TRANSIENT, Modifier.VOLATILE) .create();

Gson's @Expose

如果上面的忽略字段和类的方法对你不起作用 你也可以写自己的忽略策略并配置到Gson里面,参照 ExclusionStrategy
JavaDoc 查看更多的信息
下面的例子展示如果忽略字段被注释为@Foo 并忽略顶层类String类。
@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支持预定义字段命名策略,包含标准JAVA字段命名(骆驼峰命名 这玩意不是微软定义的吗 以小写字母开始--- sampleFieldNameInJava),并用它来给Json字段命名。参照 FieldNamingPolicy类来获取更多关于预定义命名策略
它也提供一套基于注释策略来允许客户端自定义各自字段基础,注意基于注释策略有字段命名确定机制 ,如果注释值提供的字段命名不合法则会包Runtime异常
下面是一个如何使用的例子:
`
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"}
`
如果你有一个自定义的命名策略的需要(见讨论),你可以使用“serializedname注释。

在自定义序列化和反序列化中共享状态


有时你需要在自定义序列化/反序列化器共享状态(见讨论)。你可以使用以下三种策略来完成这个:
1.在静态域存储分享的状态
2.声明序列化器或者反序列化器作为父类型的内部类,并使用父类型的实例字段来存储共享状态
3.使用Java ThreadLocal
1.2不是线程安全,3是线程安全的


此外gson的对象模型和数据绑定,您可以使用gson读取和写入流。您还可以将流媒体和对象模型的访问组合起来,以获得最佳的两种方法。

在设计gson时的一些问题

当我们设计Gson的一些讨论 详见Gson设计文档。它还包括与其他Java库,可用于JSON转换gson比较。

Gson未来加强方向


详情查看最新列表关于需要加强的方向,或者你想提一些新的建议,在项目网站上看到问题部分。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,012评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,589评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,819评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,652评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,954评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,381评论 1 210
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,687评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,404评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,082评论 1 238
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,355评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,880评论 1 255
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,249评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,864评论 3 232
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,007评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,760评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,394评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,281评论 2 259

推荐阅读更多精彩内容