专栏首页Bennyhuo简单对比下 Moshi 和 Kotlinx.serialization

简单对比下 Moshi 和 Kotlinx.serialization

上一篇我们对比介绍了 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

本文分享自微信公众号 - Kotlin(KotlinX),作者:Benny Huo

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2018-09-03

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Kotlin 1.4 新特性预览

    Kotlin 1.4 的第一个里程碑版本发布了,具体发布信息可以参考1.4-M1 ChangeLog[1]。

    bennyhuo
  • Kotlin Native 写 Jni 第二弹:动态绑定 Native 方法

    上一篇文章 我讲了用 @CName 这个神奇的注解,可以配置 Kotlin Native 函数在符号表中的名字,进而根据 Jni 静态绑定的规则来对应到 Jav...

    bennyhuo
  • Kotlin 官网大变样?这是要干啥?

    最有意思的是居然把 Android 放到了最后,hmmm,Kotlin 开发者里面搞 Android 的应该是最多的吧?这么不受待见?

    bennyhuo
  • StreamingPro 再次支持 Structured Streaming

    之前已经写过一篇文章,StreamingPro 支持Spark Structured Streaming,不过当时只是玩票性质的,因为对Spark 2.0+ 版...

    用户2936994
  • JDK1.9-打印流

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

    cwl_java
  • Java同步器之AbstractOwnableSynchronizer详解

    JDK 6 时提供。 一种同步器,可以由一个线程独占。该类提供了创建锁和相关同步器的基础,这些同步器可能包含所有权的概念。AbstractOwnableSyn...

    JavaEdge
  • 学习python第三天数据库day2

    day01回顾: 数据库: 定义:存储数据的仓库(database,简称db) 常用的数据库对象有哪些? 1).数据表(table) ***** 2)...

    hankleo
  • C++入门知识(二)

    用这种方式声明的引用,不能通过引用对目标变量的值进行修改,从而使引用的目标成为const,达到了引用的安全性。

    海盗船长
  • Groovy 使一个类不可变

    创建不可变对象,创建后不能更改。 这使得不可变对象在并发和函数编程中非常有用。 要将Java类定义为不可变,我们必须将所有属性定义为readonly和priva...

    白石
  • iOS xcode9.3 pod 删除框架之后,clang报错:not found框架

    clang: error: linker command failed with exit code 1 (use -v to see invocation) ...

    ZY_FlyWay

扫码关注云+社区

领取腾讯云代金券