简单对比下 Moshi 和 Kotlinx.serialization
- 2020 年 2 月 20 日
- 筆記
上一篇我们对比介绍了 Gson 和 Kotlinx.serialization,很多小伙伴在后台留言说,moshi 呢?
Moshi 怎么解决 Kotlin 数据类的问题?
首先必须说的是,Moshi 这个框架也算是 Jake 大神的良心之作了,无论从功能上,还是从使用的角度,这个框架值得推荐。
我们上一篇文章提到 Gson 不认识 Kotlin,所以对 Kotlin 的数据类几乎没有支持,这包括构造器的默认值、初始化逻辑的调用等等,而 Moshi 则类似于 Kotlinx.serialization,为我们解决了这一问题。
其实如果我们不看 Moshi 和 KS 的实现,我们单纯猜测他们要如何解决这一难题的话,无非就是使用 Kotlin 反射或使用注解处理器等方法来获取到 Kotlin 类的主构造器,以及它的参数类型和参数名(注意Kotlin反射是可以获取到参数名的哦~当然如果你用了混淆,那么这里会有问题),所以对于下面的情况:
data class Data(val id: Int, val name: String, val age: Int)
即便我们的 Json 中 K-V 的顺序是乱序的:
{"name": "bennyhuo", "id": 1000, "age": 20}
使用 Kotlin 反射,一样可以正确的将 Json 的数据结构与 Data
的主构造器的参数一一正确对应。使用注解处理器那就更不用说了。Moshi 的解决方法就是这样,它为我们提供了两种选择,你可以选择使用 Kotlin 反射,那样的话你需要忍受 Kotlin 反射 2.5M 的 jar 包以及相对较慢的运行时开销;你也可以选择注解处理器的方式,
@JsonClass(generateAdapter = true) data class Data(val id: Int, val name: String, val age: Int)
Moshi 可以为前面的 Data
生成一个 Adapter
:
class DataJsonAdapter(moshi: Moshi) : JsonAdapter<Data>() { private val options: JsonReader.Options = JsonReader.Options.of("id", "name", "age") ... override fun fromJson(reader: JsonReader): Data { var id: Int? = null var name: String? = null var age: Int? = null reader.beginObject() while (reader.hasNext()) { when (reader.selectName(options)) { 0 -> id = intAdapter.fromJson(reader) ?: throw JsonDataException("Non-null value 'id' was null at ${reader.path}") 1 -> name = stringAdapter.fromJson(reader) ?: throw JsonDataException("Non-null value 'name' was null at ${reader.path}") 2 -> age = intAdapter.fromJson(reader) ?: throw JsonDataException("Non-null value 'age' was null at ${reader.path}") -1 -> { // Unknown name, skip it. reader.skipName() reader.skipValue() } } } reader.endObject() var result = Data( id = id ?: throw JsonDataException("Required property 'id' missing at ${reader.path}"), name = name ?: throw JsonDataException("Required property 'name' missing at ${reader.path}"), age = age ?: throw JsonDataException("Required property 'age' missing at ${reader.path}")) return result } override fun toJson(writer: JsonWriter, value: Data?) { ... } }
大家可以自己试一试,考虑篇幅我只保留了 fromJson
的实现,大家可以参考。
Kotlin.serialization 怎么解决 Kotlin 数据类的问题?
那么同样的问题我们再问一问 KS。KS 的思路实际上与 Moshi 的注解处理器类似,只不过它因为更靠近 Kotlin 官方,是嫡系,因此它可以把一些工作放到编译器里面做。
@Serializable data class Data(val id: Int, val name: String, val age: Int)
同样用 Data
这个类为例,我们按照 KS 的要求配置好之后,编译,我们可以在 Data
的字节码当中找到一些额外的东西:
public static final class $serializer implements KSerializer { public static final Data.$serializer INSTANCE; private static final KSerialClassDesc $$serialDesc; public Data update(@NotNull KInput input, @NotNull Data old) { ... } public Object update(KInput var1, Object var2) { ... } public KSerialClassDesc getSerialClassDesc() { ... } public void save(@NotNull KOutput output, @NotNull Data obj) { ... } public Data load(@NotNull KInput input) { ... } ... }
$serializer
就是 KS 为 Data
生成的默认的序列化类,这样的做法其实与注解处理器有异曲同工之妙,只不过直接生成字节码的方式可以修改原有的类,因此作为 Data
的内部类, $serializer
可以访问 Data
的私有成员(如果有的话)。
Moshi 和 Kotlin.serialization 的对比
这二者从能力上,对 Kotlin 的支持其实差异不大,下面我简单它们适合的场景。
- KS 的优势是支持 Kotlin 的 Multiplatform,对于需要多平台移植的 Kotlin 代码,使用 KS 显然更合适。
- Moshi 的优势是兼容 Java ,毕竟 Kotlin 的代码 90% 仍然跑在 Jvm 甚至 Android 上,所以如果你的 Kotlin 代码与 Java 代码混合运行在 Jvm 上面,那么考虑使用 Moshi。
对啦,我的 Kotlin 新课 “基于 GitHub App 业务深度讲解 Kotlin1.2高级特性与框架设计” 上线之后,大家普遍反映有难度,有深度,如果哪位朋友想要吊打 Kotlin,不妨来看看哦!
https://coding.imooc.com/class/232.html
