上篇文章gson用户指南(上)翻译到了10、序列化和反序列化有任意类型对象的collection
我们继续
11、内置的序列化器和反序列化器
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 点击打开链接
12、自定义序列化和反序列化
有时候默认表示不是你想要的。通常情况下在处理库类(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调用检查类型适配器实现一个以上的接口,注册它。
13、写一个序列化器
JodaTime DateTime 类的示例
private class DateTimeSerializer implements JsonSerializer<DateTime> { public JsonElement serialize(DateTime src, Type typeOfSrc, JsonSerializationContext context) { return new JsonPrimitive(src.toString()); } }
当它运行到一个DateTime对象序列化时。Gson会调用serialize()。
14、写一个反序列化器
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会调用deserialize 方法。
序列化与反序列化的细节
通常你想为所有泛型类型对应的原始类型注册一个handler
例如,假如你有一个Id类来表示id
Id类型有相同的序列化泛型类型,本质上是输出Id的值
反序列化相似但是不同,需要调用new Id(Class<T>, String) 来创建实例
Gson支持注册一个handler。你也可以为一个特定的泛型类型(比如Id < RequiresSpecialHandling >需要特殊处理)注册一个特殊的handler。toJson的类型参数()和fromJson()包含泛型类型信息来帮助你编写一个为所有泛型类型对应相同原始类型的handler。
15、编写实例创建者
当反序列化一个对象的时候,Gson需要创建类的默认对象,一个正常序列化和反序列化的类需要有无参构造方法。
无论是public的还是private的都可以
通常,在处理一个没有无参构造方法的库中的类的时候才需要实例创建器。
示例
private class MoneyInstanceCreator implements InstanceCreator<Money> { public Money createInstance(Type type) { return new Money("1000000", CurrencyCode.USD); } }
Type
可以是相应的泛型
调用构造函数需要特定的泛型类型信息
16、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来解决这个问题。在本例中Java对象是类型参数化的类型表示的Id
< Foo >应该绑定到实例Id < Foo >。由于Id类只有一个参数化的类型参数T,我们使用getActualTypeArgument()返回的第0个元素类型的数组将持有Foo.class
17、紧凑输出和格式化输出Json对比
默认json输出是紧凑格式,这意味着json结构中没有空格。名称和值,对象字段和对象数组中的JSON输出不会有空白字段。
null字段将在输出中被忽略(注意:null值仍将包含在集合/数组对象中)。
如果要使用格式化输出,需要在用GsonBuilder构建Gson时配置。JsonFormatter 不是公有的,外部不能直接调用。到目前为止,内部只有JsonPrintFormatter
一个格式化输出,默认一行80个字符,2个字符缩进,4个字符的右间距
示例
Gson gson = new GsonBuilder().setPrettyPrinting().create(); String jsonOutput = gson.toJson(someObject);
18、对Null对象的支持
Gson默认情况下 null对象字段会被忽略。这样允许一个更紧凑的输出格式;然而客户端必须为这些字段定义一个默认值将JSON格式转换回Java形式。
这是使Gson支持null对象输出的方法
Gson gson = new GsonBuilder().serializeNulls().create();
注意,当使用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
19、版本支持
同一对象的多个版本可以使用@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);
输出
{"newField":"new","field":"old"} {"newerField":"newer","newField":"new","field":"old"}
20、排除序列化和反序列化字段
Gson支持众多不包括顶级类、字段和字段类型机制。以下是灵活的机制,允许将字段和类排除在外。如果没有一个机制满足您的需求然后你可以使用自定义序列化器和反序列化器。
java关键字
默认情况下,如果一个字段标记为瞬态,它将被排除在外。,如果字段标记为静态,那么在默认情况下将被排除在外。如果你想包括一些字段你可以这样
import java.lang.reflect.Modifier; Gson gson = new GsonBuilder() .excludeFieldsWithModifiers(Modifier.STATIC) .create();
可以添加多个常量
Gson gson = new GsonBuilder() .excludeFieldsWithModifiers(Modifier.STATIC, Modifier.TRANSIENT, Modifier.VOLATILE) .create();
21、Gson的@Expose注解
这个特性提供了一种方法,可以标记某些字段的对象排除序列化和反序列化为JSON。使用这个注解,必须调用new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create()。这样@Expose注解的类和字段才能被序列化和反序列化。
22、自定义排除策略
如果排除字段和类类型上述机制不适合你,那么你可以编写自己的排斥战略,并把它应用到GSON。详细信息请参照文档,文档地址ExclusionStragegy
示例
@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}
23、对Json字段命名的支持
Gson支持一些预先定义的字段命名策略转换标准的Java字段名,例如小写字母开头的驼峰式命名,详细信息可以参考文档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"}
相关地址
http://groups.google.com/group/google-gson/browse_thread/thread/cb441a2d717f6892
http://google.github.io/gson/apidocs/com/google/gson/annotations/SerializedName.html
24、共享序列化和反序列化状态
有时候需要共享序列化反序列化,可以用下面三个方法
(1)通过静态字段存储共享状态
(2)在父类中定义序列化反序列化内部类,并使用父类型的实例字段来存储共享状态。
(3)使用ThreadLocal
1,2是线程不安全的,3是线程安全的
25、流的处理
除了Gson的对象模型和数据绑定,可使用GSON读取和写入流。也可以流和对象模型的访问都是用,以获得最佳的方法。
七、Gson的设计
见GSON设计文档,对设计GSON时我们面临的问题进行了探讨。也包括GSON与可用于JSON的转换其他Java库的比较。
参考地址https://sites.google.com/site/gson/gson-design-document
八、未来Gson的增强功能
建议的改进的最新列表,或者如果你想建议更新的内容,请参照地址https://github.com/google/gson/issues
终于译完了,有点绕。
gson是非常不错的json解析框架。使用上比较简单,功能强大,效率上也不错。
欢迎扫描二维码,关注公众号