Gson应该是大家常用的序列化/反序列化对象的工具,但是如果我们反序列化的是诸如List<User>集合对象,由于泛型擦除的原因,我们得手动构造一个TypeToken对象并传入其type属性作(type就是我们真正要反序列化生成的对象类型)
本文使用的测试数据、Gson版本
implementation 'com.google.code.gson:gson:2.9.0'
data class User(
private val name: String = "",
private val age: Int = 0,
private val money: Double = 0.0
)
val content =
[
{
"name": "john",
"age": 10,
"money": 10.25
},
{
"name": "tom",
"age": 25,
"money": 90.25
},
{
"name": "lily",
"age": 55,
"money": 10.95
}
]
借助TypeToken
反序列化集合对象(麻烦)
//构造一个TypeToken对象
val token = object : TypeToken<List<User>>() {}
//序列化
val fromJson = Gson().fromJson<List<User>>(content, token.type)
输出:
[User(name=john, age=10, money=10.25), User(name=tom, age=25, money=90.25), User(name=lily, age=55, money=10.95)]
可以看到,我们每次都得先构造一个TypeToken类来帮助我们反序列化对象,显得有点繁琐,最希望的写法是调用一个函数,传入反序列化的内容以及对象类型,直接返回集合对象,接下来我们按照这个方向开始探索
TypeToken
的type属性是什么
上面创建TypeToken的目的就是拿到type属性,如果我们能知道type代表什么,我们手动构造个type传入不就皆大欢喜省时省力了
从debug调试里,可以看到这个type的对象类型是
$Gson$Types$ParameterizedTypeImpl
,看下这个类:
private static final class ParameterizedTypeImpl implements ParameterizedType, Serializable {
private final Type ownerType;
private final Type rawType;
private final Type[] typeArguments;
public ParameterizedTypeImpl(Type ownerType, Type rawType, Type... typeArguments) {
...
this.ownerType = ownerType == null ? null : canonicalize(ownerType);
this.rawType = canonicalize(rawType);
this.typeArguments = typeArguments.clone();
...
}
public Type[] getActualTypeArguments() {
return typeArguments.clone();
}
public Type getRawType() {
return rawType;
}
public Type getOwnerType() {
return ownerType;
}
}
可以看到这个类是实现了ParameterizedType
接口,意为参数化类型,这个接口代表什么呢
举例说明ParameterizedType
//创建一个测试类
open class OP<T> {}
val test = object: OP<User, String>() {}
val type: ParameterizedType = test.javaClass.genericSuperclass as ParameterizedType
println(type)
println(type.ownerType)
println(type.rawType)
println(Arrays.toString(type.actualTypeArguments))
输出:
这是一种常见的获取泛型具体类型的方式,声明泛型的类可以转成ParameterizedType
类型,其中:
- rawType:表示当前泛型类的原始类型,也就是OP
- actualTypeArguments:表示泛型类所携带的泛型的具体类型,也就是User和String
- ownerType:表示这个类型所有者的类型,用于存在内部类的情况,否则返回null
尝试使用ParameterizedType
表示List<User>
类型
经过上面对ParameterizedType
,我们就可以很轻松使用ParameterizedType
表示List<User>
类型:
首先我们定义通用的ParameterizedType
适配器类:
class ParameterizedTypeAdapter(private val mRawType: Type, private val typeArgument: Type) :
ParameterizedType {
override fun getActualTypeArguments(): Array<Type> = arrayOf(typeArgument)
override fun getRawType(): Type = mRawType
override fun getOwnerType(): Type? = null
}
然后就可以直接使用ParameterizedTypeAdapter(List::class.java, User::class.java)
来表示List<User>
了
结合Kotlin特性进一步封装下
inline fun <reified T> fromJson(content: String): List<T> =
Gson().fromJson(content, ParameterizedTypeAdapter(List::class.java, T::class.java))
然后就实现了我们一开始想要的效果:只要传入反序列化的内容以及对象类型,就能直接返回生成的集合对象
:
val fromJson = fromJson<User>(content)
println(fromJson)
输出:
尝试过的另一种优化思路(有BUG)
之前考虑过直接利用kotlin的泛型实化来优化反序列化过程:
inline fun <reified T> fromJson2(content: String): T {
return Gson().fromJson(content, T::class.java)
}
val fromJson4 = fromJson2<List<User>>(content)
但是有点问题,打印结果如下:
User的age字段声明是个Int类型,经过反序列化操作打印出了浮点类型,经过上网查找,好像和Gson内部的逻辑有关系,需要利用GsonBuild重写内部的一个反序列化函数,看起来太麻烦,就放弃了!!