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

效果如下图所示:

参考

相关推荐
移动开发者1号19 小时前
App主界面点击与跳转启动方式区别
android·kotlin
移动开发者1号19 小时前
我用Intent传大图片时竟然崩了,怎么回事啊
android·kotlin
androidwork1 天前
Android LinearLayout、FrameLayout、RelativeLayout、ConstraintLayout大混战
android·java·kotlin·androidx
androidwork1 天前
OkHttp 3.0源码解析:从设计理念到核心实现
android·java·okhttp·kotlin
莉樱Yurin1 天前
Kotlin/CLR 让Kotlin走进.NET世界
kotlin
casual_clover1 天前
Android 之 kotlin 语言学习笔记四(Android KTX)
android·学习·kotlin
移动开发者1号1 天前
Android 大文件分块上传实战:突破表单数据限制的完整方案
android·java·kotlin
移动开发者1号1 天前
单线程模型中消息机制解析
android·kotlin
梓仁沐白2 天前
【Kotlin】协程
开发语言·python·kotlin
WAsbry2 天前
现代 Android 开发自定义主题实战指南
android·kotlin·material design