1. 概述
kotlinx-serialization 是 Kotlin 官方推出的一个序列化库,支持将 Kotlin 对象序列化为 JSON、Protobuf、CBOR 等多种格式。
它是一个开源项目,提供了简洁易用的 API,适用于无反射的序列化场景。Kotlin 1.4.0 开始引入了 JSON 序列化的稳定版本,取代了之前使用的 kotlinx-serialization-runtime
库,但其他格式的序列化目前仍未正式稳定。
本教程将重点介绍如何使用 kotlinx-serialization 的 JSON 功能。我们将学习如何在项目中使用它,以及如何编写自定义序列化器来处理更复杂的结构。
2. 开始使用 kotlinx-serialization
要使用该库,首先需要将其依赖添加到项目的 pom.xml
文件中。在此之前,先定义兼容的版本号:
<properties>
<kotlin.version>1.8.10</kotlin.version>
<serialization.version>1.5.0</serialization.version>
</properties>
接着,在 Kotlin 编译插件中添加对 kotlinx-serialization
的支持:
<build>
<plugins>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<version>${kotlin.version}</version>
<executions>
<execution>
<id>compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
<configuration>
<compilerPlugins>
<plugin>kotlinx-serialization</plugin>
</compilerPlugins>
</configuration>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-serialization</artifactId>
<version>${kotlin.version}</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
这个插件负责为使用 @Serializable
注解的类自动生成序列化代码。
最后,添加运行时依赖:
<dependency>
<groupId>org.jetbrains.kotlinx</groupId>
<artifactId>kotlinx-serialization-json</artifactId>
<version>${serialization.version}</version>
</dependency>
添加完这些配置后,就可以在项目中使用 kotlinx-serialization 了。
3. 序列化对象
第一步是定义一个需要被序列化的 Kotlin 数据类。我们创建一个简单的 SerializablePerson
类,包含 firstName
、lastName
和 age
三个属性:
@Serializable
data class SerializablePerson(
val firstName: String,
val lastName: String,
val age: Int,
) {
init {
require(firstName.isNotEmpty()) { "First name can't be empty" }
}
val fullName: String
get() = "$firstName $lastName"
val id by ::lastName
}
✅ 注意: 必须为类添加 @Serializable
注解,kotlinx-serialization 才会生成对应的序列化代码。
在这个类中我们还添加了两个没有 backing field 的属性:fullName
(运行时计算)和 id
(委托属性)。kotlinx-serialization 只会序列化具有 backing field 的属性,因此这两个属性不会被包含在输出中。
使用 Json.encodeToString()
方法即可将对象序列化为 JSON:
val person = SerializablePerson("John", "Doe", 30)
val json = Json.encodeToString(person)
输出结果如下(注意 fullName
和 id
并未出现):
{
"firstName": "John",
"lastName": "Doe",
"age": 30
}
4. 反序列化对象
反序列化也很简单,使用 Json.decodeFromString()
方法即可:
val json = """{"firstName":"John","lastName":"Doe","age":30}"""
val person = Json.decodeFromString<SerializablePerson>(json)
⚠️ 但是,如果类中包含委托属性(如 id
),反序列化会失败!
下面是一个失败的测试示例:
@Test
fun `fails to deserialize string to object with delegated property`() {
val json = """{"firstName":"John","lastName":"Doe","age":30}"""
val exception = assertFailsWith<NoSuchFieldError> {
Json.decodeFromString<SerializablePerson>(json)
}
}
解决方法是去掉委托属性,重新定义一个干净的类:
@Serializable
data class DeserializablePerson(val firstName: String, val lastName: String, val age: Int) {
init {
require(firstName.isNotEmpty()) { "First name can't be empty" }
}
}
✅ 反序列化成功:
@Test
fun `deserializes string to object`() {
val json = """{"firstName":"John","lastName":"Doe","age":30}"""
val person = Json.decodeFromString<DeserializablePerson>(json)
assertEquals(DeserializablePerson("John", "Doe", 30), person)
}
⚠️ 初始化逻辑也会被调用,如果初始化条件不满足,会抛出异常:
@Test
fun `fails to deserialize string to object when json input is incorrect`() {
val json = """{"firstName":"","lastName":"Doe","age":30}"""
assertFailsWith<IllegalArgumentException> {
Json.decodeFromString<DeserializablePerson>(json)
}
}
5. 使用自定义序列化器
虽然 kotlinx-serialization 内置支持很多类型,但某些复杂类型(如 LocalDateTime
)需要我们自己实现序列化逻辑。
5.1. 创建自定义序列化器
实现自定义序列化器需继承 KSerializer
接口,并实现 serialize()
和 deserialize()
方法。
以下是一个针对 LocalDateTime
的自定义序列化器:
object LocalDateTimeSerializer : KSerializer<LocalDateTime> {
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("LocalDateTime", PrimitiveKind.String)
override fun serialize(encoder: Encoder, value: LocalDateTime) {
encoder.encodeString(value.toString())
}
override fun deserialize(decoder: Decoder): LocalDateTime {
return LocalDateTime.parse(decoder.decodeString())
}
}
要使用这个自定义序列化器,我们需要将其注册到 Json
配置中:
val json = Json {
serializersModule = SerializersModule {
contextual(LocalDateTimeSerializer)
}
}
然后就可以序列化 LocalDateTime
类型了:
@Test
fun `serializes LocalDateTime with registered serializer`() {
val dateTime = LocalDateTime.parse("2023-04-24T15:30:00.123")
val serializedDateTime = json.encodeToString(dateTime)
assertEquals("\"2023-04-24T15:30:00.123\"", serializedDateTime)
}
反序列化也一样:
@Test
fun `deserializes LocalDateTime with registered serializer`() {
val dateTime = LocalDateTime.parse("2023-04-24T15:30:00.123")
val deserializedDateTime = json.decodeFromString<LocalDateTime>("\"2023-04-24T15:30:00.123\"")
assertEquals(dateTime, deserializedDateTime)
}
如果我们在数据类中直接使用该类型,必须使用 @Serializable(with = ...)
明确指定序列化器:
@Test
fun `serializes LocalDateTime using annotation declared serializer`() {
@Serializable
data class LocalDateTimeWrapper(
@Serializable(with = LocalDateTimeSerializer::class)
val dateTime: LocalDateTime,
)
val wrapper = LocalDateTimeWrapper(LocalDateTime.parse("2023-04-24T15:30:00.123"))
val json = Json.encodeToString(wrapper)
assertEquals("{\"dateTime\":\"2023-04-24T15:30:00.123\"}", json)
}
⚠️ 如果使用 @Contextual
但未注册序列化器,则会抛出 SerializationException
:
@Test
fun `throws exception serializing LocalDateTime with unregistered contextual serializer`() {
@Serializable
data class LocalDateTimeWrapper(
@Contextual
val dateTime: LocalDateTime,
)
val wrapper = LocalDateTimeWrapper(LocalDateTime.parse("2023-04-24T15:30:00.123"))
assertThrows<SerializationException> {
Json.encodeToString(wrapper)
}
}
5.2. 使用第三方库
也可以使用 JetBrains 提供的 kotlinx-datetime 库,它已经内置了对 kotlinx-serialization 的支持。
添加依赖:
<dependency>
<groupId>org.jetbrains.kotlinx</groupId>
<artifactId>kotlinx-datetime-jvm</artifactId>
<version>0.4.0</version>
</dependency>
现在可以直接使用 kotlinx.datetime.LocalDateTime
类型进行序列化和反序列化,无需手动注册:
import kotlinx.datetime.LocalDateTime
✅ 注意: 该库使用的是 kotlinx.datetime
包下的类型,而非标准库的 java.time
。
6. 总结
kotlinx-serialization 是一个功能强大、使用方便的 Kotlin 序列化库,支持多种格式和常见类型。
- ✅ 支持自动代码生成,无需反射
- ✅ 可以自定义序列化器处理复杂类型
- ✅ 支持通过注解或运行时注册自定义逻辑
- ✅ JetBrains 提供了如
kotlinx-datetime
等扩展库,简化开发
如果你正在开发 Kotlin 项目并需要序列化功能,kotlinx-serialization 是一个非常值得考虑的选择。