在 Kotlin 中,我们可以使用 Serialization 来实现序列化和反序列化。目前支持的格式有如下几种,需要注意除了 JSON 外,其他格式都是实验性质的,api可能会变更。因此下面关于 Serialization 的使用,都是以 JSON 为例。
- JSON: kotlinx-serialization-json
- Protocol Buffers: kotlinx-serialization-protobuf
- CBOR: kotlinx-serialization-cbor
- Properties: kotlinx-serialization-properties
- HOCON: kotlinx-serialization-hocon (only on JVM)
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}")
效果如下图所示: