记一次在 Kotlin 中使用 Gson 序列化 List 带泛型的问题

前言

这个问题花了我 3 个小时,在 Kotlin 中使用 Gson 序列化 List,原本看起来再普通不过的需求,里面却隐藏着一些坑。在 Gson 中,序列化 List<Pojo> 这类实体是没有任何问题的,不用做任何特殊处理即可完成。但如果你序列化的是 List<Pojo<out BasePojo>>,那么问题就出现了,Pojo 对象本身是有属性和值的,但序列化出来的内容就成这样了 [{},{}],对象里面的属性都没了。 仅仅是定义的差别,List 中元素都是完全一样的,为啥一个可以一个不行呢?

怎么解决

网上查阅资料较少,基本没有文章说这个问题。

我试了一下,如果把定义从 List<Pojo<out BasePojo>> 换成 List<Any> 后,还是往 List 里面 add Pojo<BasePojoImpl>,序列化就是正常的,那么可以排除是 List 本身的问题。

有没有可能是对象定义的问题呢?

Pojo 定义类似如下:Pojo<T : BasePojo>

于是我又尝试,干脆把 Pojo 的泛型去掉,序列化 List<Pojo>,序列化也是正常的。单独序列化 Pojo<BasePojoImpl> 也是正常的,排除 Pojo 对象的问题。
唯独序列化 List<Pojo<out BasePojo>> 不行,就是闹幺蛾子。

那咋办呢,我查看了编译后的字节码,想了想 Gson 的工作机制可能是什么样,JVM 又会怎么处理。 泛型是语法糖,编译之后都是会消失的,再结合上文尝试的情况,问题应该是出在序列化泛型对象上。从常规思路来说,序列化 List 时,如果判断到类型是 List,只需要取出 List 中的元素, 对每一个元素做序列化就可以了,序列化 List<Any> 应该就是这么做的,但序列化 List<Pojo<out BasePojo>> 为什么就不这么做了呢?我也尝试过使用 fastjson,fastjson 能够正常序列化。因为时间原因,我没时间去细看 Gson 源码,但能猜到 Gson 并不是我想的这么处理的,假设 Gson 真不是这么处理的话,是不是我重写一下序列化元素  的代码让 Gson 按照我想的方式去处理就可以了?

于是我自己实现了一个 JsonSerializer 并注册到了 Gson 中。

1
2
3
4
5
6
7
8
9
10
11
12
private val gson: Gson = GsonBuilder()
.registerTypeAdapter(Pojo::class.java,PojoAdapter())
.create()

class PojoAdapter : JsonSerializer<Pojo<out BasePojo>> {

private val gsonInternal = Gson()

override fun serialize(src: Pojo<out BasePojo>?, typeOfSrc: Type?, context: JsonSerializationContext?): JsonElement {
return gsonInternal.toJsonTree(src)
}
}

然后,问题解决了。可以看到,这个 JsonSerializer 的实现我并没有做什么操作,只用了 Gson 原生的 toGsonTree 对单个元素做了转换。
由此可以得出,Gson 内部处理 List 序列化时,应该不是一个元素一个元素这么处理的。

最后

这种问题虽然对大部分的场景来说可能都不会遇到,但遇到一次也够折腾一会儿了,作为解决问题的一个小记录吧~