一文了解 kotlin 序列化

在 Kotlin 中,我们可以使用 Serialization 来实现序列化和反序列化。目前支持的格式有如下几种,需要注意除了 JSON 外,其他格式都是实验性质的,api可能会变更。因此下面关于 Serialization 的使用,都是以 JSON 为例。

Serialization 的使用

  • 第一步:在根目录下的 build.gradle 文件中声明插件
groovy 复制代码
plugins { 
    id 'org.jetbrains.kotlin.jvm' version '2.1.0' 
    id 'org.jetbrains.kotlin.plugin.serialization' version '2.1.0' 
}

然后在 app 的 build.gradle 中引入插件

bash 复制代码
plugins {
    id 'org.jetbrains.kotlin.plugin.serialization'
}
  • 第二步:添加Serialization的依赖
groovy 复制代码
dependencies { 
    implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3' 
}
  • 第三步:声明一个需要系列化的类。需要注意,序列化的类必须带上 @Serializable 注解,不然序列化/反序列化时会报错。
kotlin 复制代码
@Serializable
data class UserInfo(
    val name: String,
    val phone: Long,
    val age: Int
)
  • 第四步:系列化和反序列化
kotlin 复制代码
val userInfo = UserInfo("John", 1234567890, 30)
// 序列化
val stringContent = Json.encodeToString(userInfo)
println("stringContent = $stringContent")
// 反序列化
val decodeUserInfo = Json.decodeFromString<UserInfo>(stringContent)
println("userInfo2 = $decodeUserInfo")

效果如下图所示:

默认值

当我们反序列化时的 json 字符串中不包含对象声明的属性字段时,反序列化会失败。代码示例如下,json字符串没有包含 name 字段,这时反序列化会报 JsonDecodingException 异常。

ini 复制代码
val decodeUserInfo = Json.decodeFromString<UserInfo>("""{"phone":1234567890,"age":30}""")
println("decodeUserInfo = $decodeUserInfo")

如果需要解决这个问题,你可以给你的属性添加默认值。代码示例如下:

kotlin 复制代码
// 默认值为 null,反序列化结果为: UserInfo(name=null, phone=1234567890, age=30)
@Serializable
data class UserInfo(
    val name: String? = null,
    val phone: Long,
    val age: Int
)
// 默认值不为 null,反序列化结果为: UserInfo(name=默认用户, phone=1234567890, age=30)
@Serializable
data class UserInfo(
    val name: String = "默认用户",
    val phone: Long,
    val age: Int
)

@Required 和 @Transient

除了 @Serializable 注解外,Serialization 还有 @Required@Transient 需要了解下。其中 @Required 注解用来声明属性,表明该属性在反序列化中必须存在(即使有默认值也不行),否则失败。代码示例如下:

kotlin 复制代码
@Serializable
data class UserInfo(
    val name: String? = null, // 存在默认值
    val phone: Long,
    val age: Int
)

// 没有出现,则反序列化失败
val decodeUserInfo2 = Json.decodeFromString<UserInfo>("""{"phone":1234567890,"age":30}""")
println("decodeUserInfo2 = $decodeUserInfo2")

运行效果如下:

@Transient 则是让这个属性不参加序列化,代码示例如下:

kotlin 复制代码
@Serializable
data class UserInfo(
    val name: String,
    val phone: Long,
    val age: Int,
    @Transient
    var address: String = "" // @Transient 修饰的属性必须有默认值
)

val userInfo = UserInfo(null, 1234567890, 30, address = "Beijing")
val stringContent = Json.encodeToString(userInfo)
println("stringContent = $stringContent")

运行效果如下:

自定义 Serializer

在 kotlin 中除了一般的数据结构外,还有枚举和密封类。有时候我们想要把后台的一些数字类型转换为对应的枚举或者密封类,这时就可以自定义 Serializer。代码示例如下:

kotlin 复制代码
@Serializable(with = UserTypeKSerializer::class)
enum class UserType(val code: Int) {
    Teen(0),
    Adult(1)
}

class UserTypeKSerializer : KSerializer<UserType> {
    override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("UserType", PrimitiveKind.STRING)

    override fun deserialize(decoder: Decoder): UserType {
        val code = decoder.decodeInt()
        return UserType.values().firstOrNull { it.code == code } ?: UserType.Teen
    }

    override fun serialize(encoder: Encoder, value: UserType) {
        encoder.encodeInt(value.code)
    }

}

其中 UserTypeKSerializer 就是我们自定义的序列化类。内部的 SerialDescriptor 属性是一个用于描述序列化数据结构的接口,一般使用 PrimitiveSerialDescriptor 来定义基本类型的序列化描述。然后分别在 serialize 方法中自定义序列化过程;在 deserialize 方法中定义反序列化过程。

泛型的支持

同时 kotlin 的序列化还支持对泛型进行处理。代码示例如下:

kotlin 复制代码
val userInfo = UserInfo<Int>("John", UserType.Teen, 2)
// 序列化
val stringContent = Json.encodeToString(userInfo)
println("stringContent = $stringContent")
// 反序列化
val decodeUserInfo = Json.decodeFromString<UserInfo<Int>>(stringContent)
println("userInfo = ${decodeUserInfo}")

val userInfo1 = UserInfo<String>("John", UserType.Teen, "str")
// 序列化
val stringContent1 = Json.encodeToString(userInfo1)
println("stringContent = $stringContent1")
// 反序列化
val decodeUserInfo1 = Json.decodeFromString<UserInfo<String>>(stringContent1)
println("userInfo1 = ${decodeUserInfo1}")

效果如下图所示:

参考

相关推荐
橙子199110162 小时前
在 Kotlin 中,什么是解构,如何使用?
android·开发语言·kotlin
androidwork3 小时前
Android 中使用通知(Kotlin 版)
android·kotlin
_龙小鱼_5 小时前
卡顿检测与 Choreographer 原理
android·kotlin
androidwork1 天前
Kotlin Android单元测试MockK指南
android·kotlin
麻辣璐璐1 天前
Kotlin并发请求的一些知识记录
android·kotlin
androidwork1 天前
Arrow库:函数式编程在Kotlin Android中的深度实践
android·java·kotlin
androidwork1 天前
用 Kotlin 脚本(KTS)重塑 Android 工程效能:2000 字终极实践指南
android·开发语言·kotlin
每次的天空1 天前
Android学习总结之kotlin篇(二)
android·学习·kotlin
橙子199110161 天前
Kotlin 中 infix 关键字的原理和使用场景
android·开发语言·kotlin
zimoyin2 天前
Kotlin 协程实战:实现异步值加载委托,对值进行异步懒初始化
java·前端·kotlin