Gson使用文档(集合必须是带泛型的,从而必须用new TypeToken,匿名类,getType())...
原文 :http://www.jianshu.com/p/558844c96fc1
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> <!-- Gson: Java to Json conversion --> <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(), 所以你有三个选择:
- 使用 Gson's 解析API(低版本的流解析或者DOM解析) 来解析数组元素 然后使用Gson.fromJson()在对应的数组元素上,这是一个比较好的解决方案,通过一个例子来说明如何使用。
- 为Collection.class注册一个类型adapter,来把每一个数组成员都映射到对应的对象,这个方法不好之处在于它会混淆其他集合类型的反序列化
- 为MyCollectionMemberType注册一个类型adapter 通过
Collection<MyCollectionMemberType>来使用fromjson()
这个方法仅用于数组以顶层元素出现 或者 你可以通过集合的类型Collection<MyCollectionMemberType>来改变字段的类型
.
内置的序列化器和反序列化器
Gson拥有内置的序列化器和反序列化器,为那些默认显示可能不合适的常用类,下面是这些类的清单:
- java.net.URL to match it with strings like "https://github.com/google/gson/"
- 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
而不是JsonCompactFormatterGson 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未来加强方向
详情查看最新列表关于需要加强的方向,或者你想提一些新的建议,在项目网站上看到问题部分。
原文链接:http://www.jianshu.com/p/558844c96fc1
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。
上一篇: aabb (第一讲)
下一篇: JSON与对象的相互转换