Kotlinx.serialization VS Gson

JSON is an open-standard file format or data interchange format that uses human-readable text to transmit data objects consisting of attribute-value pairs and array data types.

Java中常见的JSON解析库有MoshiJacksonGson等,因为Java和Kotlin互相兼容,所以这些库也可以用在Kotlin,虽然可能会有一些小坑。

我们基于Kotlin的data class定义一个User的Bean,agerole字段分被设置默认值以是之类型非空

data class User(
    val name: String,
    val email: String,
    val age: Int = 13,
    val role: Role = Role.Viewer
)

enum class Role { Viewer, Editor, Owner }

接下来,我们将下列JSON对象解析成User

{
    "name" : "John Doe",
    "email" : "[email protected]"
}

先使用Gson写一个TestCase:

class JsonUnitTest {

    private val jsonString = """
            {
                "name" : "John Doe",
                "email" : "[email protected]"
            }
        """.trimIndent()

    @Test
    fun gsonTest() {
        val user = Gson().fromJson(jsonString, User::class.java)

        assertEquals("John Doe",user.name)
        assertEquals(null, user.role)
        assertEquals(0, user.age)
//      User(name=John Doe, [email protected], age=0, role=null)
    }

}

上面的Test运行成功了。Gson在反序列化过程中,将基本型age设置了默认值0,将引用型role设置了null,这破坏了kotlin的非空类型编译期检查的限制,我们在调用user.role时可能出现crash。所以在Kotlin中进行反序列化时,我们希望能够遵循Kotlin的规范,识别这种非空类型。

Kotlinx.serialization

Kotlinx.serialization是Jetbrains针对Kotlin的推出序列化库。通过为KClass添加@Serializable注解,配合其配套的Kotlin Compiler Plugin,可以在编译期为在伴生对象中生成serializer()方法,serializer()方法返回一个KSerializer类型的序列化器。
KSerializer可以进行多种类型数据的序列化反序列化,例如 JSON、CBOR以及Protobuf

Kotlinx.serialization 还支持Kotlin MPP的跨平台使用,可以在JVM之外工作,例如用在Kotlin/Native或JavaScript中。

Kotlinx.serialization需要 Kotlin 1.3.30以上才可使用,如上所述,除了依赖runtime库以外, 还需要以来给一个Compiler Plugin用来进行字节码插桩。字节码插桩的一个直接好处就是可以实现零反射,性能更好。

我们基于Kotlinx.serialization测试一下上面的TestCase:

@Serializable
data class User(
    val name: String,
    val email: String,
    val age: Int = 13,
    val role: Role = Role.Viewer
)

enum class Role { Viewer, Editor, Owner }

class JsonUnitTest {

    private val jsonString = """
            {
                "name" : "John Doe",
                "email" : "[email protected]"
            }
        """.trimIndent()

    @Test
    fun gsonTest() {
        val user = Gson().fromJson(jsonString, User::class.java)

        assertEquals("John Doe", user.name)
        assertEquals(null, user.role)
        assertEquals(0, user.age)
//      User(name=John Doe, [email protected], age=0, role=null)
    }

    @Test
    fun jsonTest() {
        val user = Json.parse(User.serializer(), jsonString)

        assertEquals("John Doe", user.name)
        assertEquals(Role.Viewer, user.role)
        assertEquals(13, user.age)
//      User(name=John Doe, [email protected], age=13, role=Viewer)
    }
}

测试通过可以看见Kotlinx.serialization反序列化结果中的缺省值是data class中定义的值,更符合预期。


Kotlinx.serialization + Retrofit

我们都经常使用Retrofit+Gson的组合进行HTTP请求以及对请求结果进行反序列化。 如今我们也可以为Retrofit 添加 新的ConverterFactory,以适配Kotlinx.serialization的使用。

我肯可以定义自己的ConverterFactory,也可以使用第三方现成的,例如J神已将帮我们写好的一个https://github.com/JakeWharton/retrofit2-kotlinx-serialization-converter

val contentType = "application/json".toMediaType()

val retrofit = Retrofit.Builder()
 .baseUrl("https://www.example.com")
 .addConverterFactory(
 	Json(JsonConfiguration(strictMode = false))
 	.asConverterFactory(contentType))
 .build()

可以通过JsonConfiguation进行配置,例如打开/关闭编码默认值,启用/禁用strictMode等。默认情况下strictMode处于启用状态,此时会检查JSON中是否存在unknown的key,并且不运行String像num类型的转换等。一个推荐做法是在debug模式下开启严格模式,在release包中关闭,可以提高线上的容错性。


最后

本文只是介绍了Kotlinx.serialization的基本用法,想了解更多内容可以参考官网以及 KotlinConf 2019: Design of Kotlin Serialization by Leonid Startsev

发布了116 篇原创文章 · 获赞 15 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/vitaviva/article/details/105086016