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

效果如下图所示:

参考

相关推荐
FunnySaltyFish8 小时前
什么?Compose 把 GapBuffer 换成了 LinkBuffer?
算法·kotlin·android jetpack
Kapaseker14 小时前
Compose 进阶—巧用 GraphicsLayer
android·kotlin
Kapaseker1 天前
实战 Compose 中的 IntrinsicSize
android·kotlin
A0微声z3 天前
Kotlin Multiplatform (KMP) 中使用 Protobuf
kotlin
alexhilton4 天前
使用FunctionGemma进行设备端函数调用
android·kotlin·android jetpack
lhDream4 天前
Kotlin 开发者必看!JetBrains 开源 LLM 框架 Koog 快速上手指南(含示例)
kotlin
RdoZam4 天前
Android-封装基类Activity\Fragment,从0到1记录
android·kotlin
Kapaseker5 天前
研究表明,开发者对Kotlin集合的了解不到 20%
android·kotlin
糖猫猫cc5 天前
Kite:两种方式实现动态表名
java·kotlin·orm·kite
如此风景5 天前
kotlin协程学习小计
android·kotlin