Gson使用规则

基本规范

  • 推荐使用 private 类型,支持使用 final 声明
  • 支持 null 值,序列化输出时自动跳过,反序列化时直接返回null
  • 支持多维数组、Collection类型
  • 支持 URL、URI 的反序列化自动类型转换(也可支持JodaTime转换,参考 JodaTime Type Converter
  • 不需要定义 getter / setter 方法(通过反射设置参数值)
  • 不需要带参构造函数,并且无参构造函数不必为 public
  • 不必要使用注解标记序列化对象,除非需要指定别名映射
  • 当前类及其所有父类的所有字段均视为默认序列化对象
  • 序列化、反序列化(如 Collection 等)泛型类型时,需要通过 TypeToken 指定目标泛型的确切类型值
  • 反序列化 混合类型数组 时,可参考官方指引:Serializing and Deserializing Collection with Objects of Arbitrary Types

不属于序列化输出范围

  • transient 类型字段
  • 非 static 嵌套类、内部类型
  • 经非序列化方式指定的字段

指定Json字段命名映射

Gson 默认使用类字段名作为序列化、反序列化的 Json 数据字段名。
可通过 @SerializedName(name) 注解指定该字段对应的 Json 字段名,也可以通过添加字段命名内置策略方式改变字段名输出形式。
通过实现 FieldNamingStrategy 接口,可自定义命名映射策略,通过 gsonBuilder.setFieldNamingStrategy(FieldNamingStrategy) 方式进行设定。

// 添加 @SerializedName 注解指定命名
// 可分别通过 value 和 alternate数组 指定序列化、反序列化时有效的字段命名
private class SomeObject {
  @SerializedName("custom_naming") private final String someField;
  private final String someOtherField;
}

// 添加名字映射策略,这里使用首字母大写驼峰策略
Gson gson = new GsonBuilder()
    .setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE)
    .create();

通过 TypeToken 指定泛型类型值

Foo<Bar> foo = new Foo<Bar>();

// 指定泛型的确切类型值
Type fooType = new TypeToken<Foo<Bar>>(){}.getType();

// 泛型序列化、反序列化,传入泛型类型值(序列化过程可不传入,但不保证序列化结果正确性)
gson.toJson(foo, fooType);
gson.fromJson(json, fooType);

Collection类型在序列化时受Gson内部支持,不需要指定泛型类型。

通过 TypeAdapter 彻底自定义序列化、反序列化行为

通过自定义实现 TypeAdapterJsonSerializer / JsonDeserializer 接口,指定Json的序列化、反序列化完整逻辑。

其中 TypeAdapter 为低层抽象类,自定义实现从流开始完全接管、控制序列化行为,序列化对象为 JsonWriter / JsonReader,运行效率而言相对较高。

JsonSerializer / JsonDeserializer 可按需分别指定特定类型值的序列化、反序列化的过程,序列化对象为 JsonElement

当目标对象类型不提供无参构造函数时,需要注册一个 InstanceCreator 实现类,用于以无参方式实例化一个目标对象。

  • (可选)实现 InstanceCreator 接口,提供以无参方式构造一个默认只能带参构造的目标容器对象
  • (可选)实现 JsonSerializer 接口,提供目标对象序列化至JsonElement类型的方法
  • (可选)实现 JsonDeserializer 接口,提供JsonElement类型反序列化至目标类型的方法
  • (可选)派生 TypeAdapter 子类,提供基于流的序列化、反序列化完整逻辑
GsonBuilder builder = new GsonBuilder();

// 需要时注册一个自定义 InstanceCreator 对象
builder.registerTypeAdapter(MyType.class, new MyInstanceCreator());

// 通过 registerTypeAdapter() 方法指定一个针对特定类型对象的委托序列化、反序列化器
// 第一个class类型参数必须为具体包装类型,可指定为一个泛型
// 使用 registerTypeHierarchyAdapter 则可使用抽象父类作为类型值,但不支持泛型
builder.registerTypeAdapter(String.class, new MySerializer());
builder.registerTypeAdapter(Integer.class, new MyDeserializer());

// 通过派生 TypeAdapter 指定一个针对特定类型对象的委托序列化、反序列化器
// 如果派生的 TypeAdapter 不对null参数值进行特殊处理
// 可简单使用 new MyTypeAdapter().nullSafe() 代替
builder.registerTypeAdapter(MyType2.class, new MyTypeAdapter());

Gson gson = builder.create();

注册使用 JsonSerializerJsonDeserializerTypeAdapterTypeAdapterFactory 等自定义序列化处理器时,对于同一目标类型的(反)序列化以最后一个注册为准。
Gson的处理优先顺序为 registerTypeAdapter() > 注解 > 内置TypeAdapter ,在注册指定序列化处理器时,Gson注解等内置自动序列化系统将失效(控制权由自定义处理器掌控)。

参考:

关于 null 的支持

默认状态下,null 值的对象在序列化 Json 过程中会直接忽略,不输出到 Json 结果,但可通过以下方式简单的启用 null 输出:gsonBuilder.serializeNulls().create()

反序列化时,传入空的Json或null或"null",将直接返回null

输出 Json 效果如:

gson.toJson(stuff) -> {"name":null,"number":5}
gson.toJson(null) -> null

gson.fromJson("", Type.class) -> null

序列化版本支持

可指定某些字段仅在特定版本下实施序列化,比序列化所用 Gson 对象指定版本更高的字段将被忽略。

public class VersionedClass {

  // 通过 @Since 和 @Until 注解指定该字段的有效起始版本(包含)、有效到期版本(不包含)
  @Since(1.1) private final String newerField;

  @Since(1.0) private final String newField;

  @Until(2.0) private final String oldField;

  private final String field;
}

// 指定 1.0 版本时,序列化输出 Json 不包括 1.1 版本及以上的字段 newerField
// 但包含 2.0 版本以下时可用的字段 oldField
Gson gson = new GsonBuilder().setVersion(1.0).create();
...

// 不指定版本时,版本机制无效,所有字段都将输出到结果 Json
gson = new Gson();
...

使用内置的方案筛选非序列化字段

默认状态下Gson自动排除经 transient 声明的字段。
除此之外,可在 GsonBuilder 中通过以下两种方式添加额外的排除方案:

// 通过 excludeFieldsWithModifiers 指定需要排除的修饰符
Gson gson = new GsonBuilder()
    .excludeFieldsWithModifiers(Modifier.STATIC, Modifier.TRANSIENT, Modifier.VOLATILE)
    .create();


// 通过 @Expose 注解标记可以序列化的合法字段
// 其中可选分别指定 serialize 和 deserialize 的有效性
// @Expose = @Expose(serialize = true, deserialize = true)
@Expose private final String fieldForOutput;
final String fieldForCacheLocally;

gson = new GsonBuilder()
    .excludeFieldsWithoutExposeAnnotation()
    .create();

使用自定义方案识别非序列化字段

派生 ExclusionStrategy 子类,覆盖指定方法,以代码逻辑方式判断需要排除的字段类型、注解标记、字段值等特征。

Gson gson = new GsonBuilder()
    .setExclusionStrategies(new MyExclusionStrategy(String.class))
    .addSerializationExclusionStrategy(ExclusionStrategy)
    .addDeserializationExclusionStrategy(ExclusionStrategy)
    .create();
参考:

其他可选格式化设置

通过 GsonBuilder 可链式设置多种格式化方案:

  • serializeNulls():允许序列化输出 null
  • setDateFormat("yyyy-MM-dd"):设置序列化、反序列化时使用的日期格式
  • disableInnerClassSerialization():禁止序列化内部类
  • generateNonExecutableJson():生成JS中不可执行的 Json(在前缀插入一些特殊字符)
  • disableHtmlEscaping():禁用Html自动转义
  • setPrettyPrinting():以可读格式输出,默认序列化Json不包含空格,使用可读格式输出时将激活内置的JsonPrintFormatter,生成添加额外空格缩进排版的Json字符串(格式不可自定义)
  • serializeSpecialFloatingPointValues():允许序列化特殊浮点标记值,如 NAN、Infinity、-Infinity
  • enableComplexMapKeySerialization():允许序列化复合型key(如一个自定义对象等)

其他文献参考

你真的会用Gson吗?Gson使用指南

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

推荐阅读更多精彩内容

  • 1.概述2.Gson的目标3.Gson的性能和扩展性4.Gson的使用者5.如何使用Gson 通过Maven来使用...
    人失格阅读 14,223评论 2 18
  • 为了更好的学习Gson,特将Gson User Guide翻译如下。由于本人英文水平有限,如有错误,还请指正,谢谢...
    WeberLisper阅读 6,775评论 0 6
  • 产品设计,要迎合市场。除了自身要具备极工业制造原理,还要有极强的艺术品鉴赏能力。 刚性的工业遇到柔性的艺术设...
    牛田春水阅读 205评论 0 0
  • 该内容参考地址 过程概述 浏览器查找域名对应的 IP 地址; 浏览器根据 IP 地址与服务器建立 socket 连...
    Ann_l阅读 414评论 1 1
  • 寂寞是江南小巷里的一堵墙 墙外春光明媚 一支红杏 伸出墙外 格外妖娆 寂寞是宋代街市中的一扇窗 窗外有人走来 一个...
    思飨反动阅读 87评论 0 0