一文了解 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}")

效果如下图所示:

参考

相关推荐
Kotlin上海用户组2 小时前
Koin vs. Hilt——最流行的 Android DI 框架全方位对比
android·架构·kotlin
Kapaseker9 小时前
当Object遇到Json你可能会碰到的坑
kotlin
RichardLai889 小时前
Kotlin Flow:构建响应式流的现代 Kotlin 之道
android·前端·kotlin
程序员江同学11 小时前
Kotlin/Native 编译流程浅析
android·kotlin
移动开发者1号11 小时前
Kotlin协程与响应式编程深度对比
android·kotlin
tq108613 小时前
使用协程简化异步资源获取操作
kotlin·结构化并发
alexhilton1 天前
为什么你的App总是忘记所有事情
android·kotlin·android jetpack
移动开发者1号1 天前
Kotlin协程超时控制:深入理解withTimeout与withTimeoutOrNull
android·kotlin
移动开发者1号2 天前
Java Phaser:分阶段任务控制的终极武器
android·kotlin
哲科软件2 天前
跨平台开发的抉择:Flutter vs 原生安卓(Kotlin)的优劣对比与选型建议
android·flutter·kotlin