原文链接:Gson Advanced — Custom Serialization for Simplification (Part 1)
原文出自:Norman Peitek
译者:無名無
本文将实现一个自定义的 Gson serialization 过程,有些情况我们可能会考虑到自定义,例如:和 server 通信时,有时候不需要传递一个完成 JSON 信息,只需要关键信息即可,接下来我们一步一步分析。
自定义序列化
有个这样的场景:App 从 服务器获取一个列表,用户可以订阅列表中的每一项,但是要将订阅的某一条发送给服务器同步。
我们一步一步实现:
Models
public class UserSimple {
private String name;
private String email;
private boolean isDeveloper;
private int age;
}
public class Merchant {
private int Id;
private String name;
// possibly more properties
}
用于解析完整列表的 Model:
public class UserSubscription {
String name;
String email;
int age;
boolean isDeveloper;
// new!
List<Merchant> merchantList;
}
问题
先看输出:
{
"age": 26,
"email": "norman@fs.io",
"isDeveloper": true,
"merchantList": [
{
"Id": 23,
"name": "Future Studio"
},
{
"Id": 42,
"name": "Coffee Shop"
}
],
"name": "Norman"
}
这是一个非常标准 JSON 的输出,但是在实际中,不可能是这么小的数据量,而且 Merchant 结构可能非常复杂,这就会造成 JSON 数据非常大,从而导致解析或传递耗时。
使用 @Expose 简化
我们第一个想到的方法是减少 Merchant 中不必要字段的序列化,使用之前学过的 @Expose 来简化 JSON,我们来调整下 Merchant:
public class Merchant {
private int Id;
@Expose(serialize = false)
private String name;
// possibly more properties
}
再来看输出:
{
"age": 26,
"email": "norman@fs.io",
"isDeveloper": true,
"merchantList": [
{
"Id": 23
},
{
"Id": 42
}
],
"name": "Norman"
}
从结果上看已经减少输出了 name 字段,看起来已经优化了,但是问题又来了,如果其他接口,需要一个完整的 JSON 结果,这该怎么办?接着往下看。
简化自定义序列化为单个对象
通过 @Expose 是能解决一部分问题,但是存在局限性,现在我们使用自定义来解决这些问题,作法不干涉 Merchant 类,只在干涉序列化过程。
先看一个最原始的例子:
// get the list of merchants from an API endpoint
Merchant futureStudio = new Merchant(23, "Future Studio", null);
Merchant coffeeShop = new Merchant(42, "Coffee Shop", null);
// create a new subscription object and pass the merchants to it
List<Merchant> subscribedMerchants = Arrays.asList(futureStudio, coffeeShop);
UserSubscription subscription = new UserSubscription(
"Norman",
"norman@fs.io",
26,
true,
subscribedMerchants);
Gson gson = new Gson();
String fullJSON = gson.toJson(subscription);
为了实现自定义需求,我们需要使用 GsonBuilder 来帮我们生成 Gson 实例,需要给 Merchant 类注册一个 adapter,大概是这种:
GsonBuilder gsonBuilder = new GsonBuilder();
JsonSerializer<Merchant> serializer = ...; // will implement in a second
gsonBuilder.registerTypeAdapter(Merchant.class, serializer);
Gson customGson = gsonBuilder.create();
String customJSON = customGson.toJson(subscription);
大部分都还是之前介绍过的方法,唯一陌生的是 registerTypeAdapter() 方法,需要传入两个参数,第一个序列化对象类型,第二个是 JsonSerializer 接口的具体实现。
来看一个 JsonSerializer 实现实例:
JsonSerializer<Merchant> serializer = new JsonSerializer<Merchant>() {
@Override
public JsonElement serialize(Merchant src, Type typeOfSrc, JsonSerializationContext context) {
JsonObject jsonMerchant = new JsonObject();
jsonMerchant.addProperty("Id", src.getId());
return jsonMerchant;
}
};
JsonSerializer 实现只有一个 serialize 方法,我们重写次方法,在方法内添加我们处理,这里使用 JsonObject 只添加 src 的 id 字段。
根据自己的需求选择调用响应的方法,再次输出:
"age": 26,
"email": "norman@fs.io",
"isDeveloper": true,
"merchantList": [
{
"Id": 23
},
{
"Id": 42
}
],
"name": "Norman"
}
可以看到上面的结果和使用 @Expose 的结果是一样的,下一节我们通过自定义序列化 List 来简化 JSON。
简化自定义序列化 List 对象
和自定义序列化对象类似,我们只需要更改下 JsonSerializer 中的类型为 List 即可,类似这样:
GsonBuilder gsonBuilder = new GsonBuilder();
Type merchantListType = new TypeToken<List<Merchant>>() {}.getType();
JsonSerializer<List<Merchant>> serializer = ...; // will implement in a second
gsonBuilder.registerTypeAdapter(merchantListType, serializer);
Gson customGson = gsonBuilder.create();
String customJSON = customGson.toJson(subscription);
同样具体的实现如下:
JsonSerializer<List<Merchant>> serializer =
new JsonSerializer<List<Merchant>>() {
@Override
public JsonElement serialize(List<Merchant> src, Type typeOfSrc, JsonSerializationContext context) {
JsonObject jsonMerchant = new JsonObject();
List<String> merchantIds = new ArrayList<>(src.size());
for (Merchant merchant : src) {
merchantIds.add("" + merchant.getId());
}
String merchantIdsAsString = TextUtils.join(",", merchantIds);
jsonMerchant.addProperty("Ids", merchantIdsAsString);
return jsonMerchant;
}
}
同样看下输出结果:
{
"age": 26,
"email": "norman@fs.io",
"isDeveloper": true,
"merchantList": {
"Ids": "23,42"
},
"name": "Norman"
}
我们可以看到,merchantList 中,大大减少了输出,这对于客户端和服务器都是有利的,有利于提升访问速度和性能。但是 merchantList 中的数据格式有些问题,我们下一节来调整下。
简化自定义序列化 List 为数组
针对上一节的问题,我们只需要修改 serialize方法中的实现即可,不在使用 JsonObject,而是使用 JsonArray,实现如下:
JsonSerializer<List<Merchant>> serializer =
new JsonSerializer<List<Merchant>>() {
@Override
public JsonElement serialize(List<Merchant> src, Type typeOfSrc, JsonSerializationContext context) {
JsonArray jsonMerchant = new JsonArray();
for (Merchant merchant : src) {
jsonMerchant.add("" + merchant.getId());
}
return jsonMerchant;
}
}
最终的结果:
{
"age": 26,
"email": "norman@fs.io",
"isDeveloper": true,
"merchantList": [
"23",
"42"
],
"name": "Norman"
}
在上面的三个 case 中,我们看到可以看简单的使用 Gson 的自定义序列化,具体逻辑部分需要我们自己实现。
一些问题
1、多次使用 registerTypeAdapter() 以最后一次为准。
2、注意方法内部不要造成无限循环,例如:
new JsonSerializer<UserSubscription>() {
@Override
public JsonElement serialize(UserSubscription src, Type typeOfSrc, JsonSerializationContext context) {
JsonElement jsonSubscription = context.serialize(src, typeOfSrc);
// customize jsonSubscription here
return jsonSubscription;
}
}
目标
了解 Gson 自定义序列化几种方式,来优化 JSON 传递的数据量大小,下一篇将继续讨论自定义问题。
练习代码已上传 Github https://github.com/whiskeyfei/Gson-Review 可自行查看。
Gson 系列文章翻译回顾
1、Gson - Java-JSON 序列化和反序列化入门
2、Gson - 映射嵌套对象
3、Gson - Arrays 和 Lists 映射对象
4、Gson - Map 结构映射
5、Gson - Set 集合映射
6、Gson - 空值映射
7、Gson Model Annotations - 如何使用 @SerializedName 更改字段的命名
8、Gson Model Annotations - @SerializedName 匹配多个反序列化名称
9、Gson Builder - 基础和命名规则
10、Gson Builder - 序列化空值
11、Gson Builder - 忽略策略
12、Gson Builder - Gson Lenient 属性
13、Gson Builder - 特殊类型 Floats & Doubles
17、Gson Builder - 如何使用 @Expose 忽略字段
19、Gson Advanced - 映射枚举类型
20、Gson Advanced - 映射循环引用
21、Gson Advanced - 泛型
22、Gson Advanced - 简单自定义序列化 (Part 1)
24、Gson Advanced - 自定义反序列化基础
25、Gson Advanced - 自定义对象实例创建
26、Gson Advanced - 通过 @JsonAdapter 自定义(反)序列化过程
32、Practical Gson - 如何解析多态对象
学习讨论
刚刚建了一个 Android 开源库分享学习群,有兴趣的小伙伴可以加入一起学习。

网友评论