文档菜单
文档首页
/ / /
Kotlin 协程
/ /

Kotlin 序列化

本页内容

  • 概述
  • 支持类型
  • 将 Kotlin 序列化添加到您的项目中
  • 注释数据类
  • 自定义序列化器示例
  • 自定义序列化器配置
  • 自定义编解码器示例
  • 多态序列化
  • 多态数据类示例
  • 序列化日期和时间
  • kotlinx-datetime 库
  • 包含日期和时间的示例数据类

该 Kotlin 驱动支持kotlinx.serialization 库,用于序列化和反序列化 Kotlin 对象。

驱动提供了一种高效的 Bson 序列化器,您可以使用标记为 @Serializable 的类来处理将 Kotlin 对象序列化为 BSON 数据。

您还可以安装 bson-kotlinx 库来支持自定义编解码器,并配置以编码默认值、编码 null 值和定义类区分符。

注意

要了解如何使用 Codec 接口而不是 Kotlin 序列化库来指定自定义编码和解码 Kotlin 对象到 BSON 数据,请参阅编解码器指南。

如果您已经熟悉该框架或更喜欢使用惯用的 Kotlin 方法,您可能会选择 Kotlin 序列化。

尽管您可以使用 Kotlin 驱动与 Kotlin 序列化 Json 库一起使用,但 Json 序列化器不支持 BSON 值类型,例如 ObjectId。您必须提供自定义序列化器来处理 BSON 和 JSON 之间的转换。

Kotlin 驱动支持

  • 所有由 Kotlin 序列化库支持的 Kotlin 类型

  • 所有可用的 BSON 类型

Kotlin 驱动程序的序列化支持依赖于官方的 Kotlin 序列化库。

选择以下选项卡,了解如何通过使用GradleMaven 包管理器将序列化依赖项添加到您的项目中

如果您使用 Gradle 管理依赖项,请在您的 build.gradle.kts 依赖项列表中添加以下内容

build.gradle.kts
implementation("org.jetbrains.kotlinx:kotlinx-serialization-core:1.5.1")
implementation("org.mongodb:bson-kotlinx:5.2.1")

如果您使用Maven来管理依赖项,请将以下内容添加到您的pom.xml依赖列表中

pom.xml
<dependency>
<groupId>org.jetbrains.kotlinx</groupId>
<artifactId>kotlinx-serialization-core</artifactId>
<version>1.5.1</version>
</dependency>
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>bson-kotlinx</artifactId>
<version>5.2.1</version>
</dependency>

要声明一个类为可序列化,请使用 Kotlin 序列化框架中的@Serializable注解标注您的 Kotlin 数据类。

在将数据类标记为可序列化后,您可以在代码中像平常一样使用它们。Kotlin 驱动程序和 Kotlin 序列化框架处理 BSON 序列化和反序列化。

以下示例显示了一个带有以下注解的简单数据类:

  • @Serializable来标记类为可序列化。

  • @SerialName来指定 BSON 文档中idmanufacturer属性的名称。这可以用作不支持在可序列化类中使用的@BsonId@BsonProperty注解的替代品。

  • @Contextual来标记 BSON id属性以使用内置的ObjectIdSerializer。此注解对于正确序列化 BSON 类型是必需的。

@Serializable
data class PaintOrder(
@SerialName("_id") // Use instead of @BsonId
@Contextual val id: ObjectId?,
val color: String,
val qty: Int,
@SerialName("brand")
val manufacturer: String = "Acme" // Use instead of @BsonProperty
)

注意

您不能在@Serializable数据类上使用来自org.bson.codecs.pojo.annotations包的注解。

有关序列化类和可用注解类的更多信息,请参阅官方 Kotlin Serialization文档。

您可以通过创建自定义序列化器来处理数据在BSON中的表示方式。Kotlin驱动程序使用来自kotlinx.serialization包的KSerializer接口来实现自定义序列化器。您可以将自定义序列化器作为参数指定给特定字段的@Serializable注解。

以下示例展示了如何创建一个自定义的KSerializer实例,用于将kotlinx.datetime.Instant转换为BsonDateTime

object InstantAsBsonDateTime : KSerializer<Instant> {
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("InstantAsBsonDateTime", PrimitiveKind.LONG)
override fun serialize(encoder: Encoder, value: Instant) {
when (encoder) {
is BsonEncoder -> encoder.encodeBsonValue(BsonDateTime(value.toEpochMilliseconds()))
else -> throw SerializationException("Instant is not supported by ${encoder::class}")
}
}
override fun deserialize(decoder: Decoder): Instant {
return when (decoder) {
is BsonDecoder -> Instant.fromEpochMilliseconds(decoder.decodeBsonValue().asDateTime().value)
else -> throw SerializationException("Instant is not supported by ${decoder::class}")
}
}
}

以下代码展示了具有注解的PaintOrder数据类,其中orderDate字段具有指定在上述代码中定义的自定义序列化器类的注解

@Serializable
data class PaintOrder(
val color: String,
val qty: Int,
@Serializable(with = InstantAsBsonDateTime::class)
val orderDate: Instant,
)

有关本节中提到的方法和类的更多信息,请参阅以下API文档

您可以使用来自org.bson.codecs.kotlinx包的KotlinSerializerCodec类来为您的@Serializable数据类创建编解码器,并自定义存储内容。

使用BsonConfiguration类定义配置,包括是否编码默认值、编码空值或定义类区分符。

要创建自定义编解码器,将bson-kotlinx依赖项安装到您的项目中。选择以下选项卡以查看如何通过使用GradleMaven包管理器将依赖项添加到项目中

如果您使用 Gradle 管理依赖项,请在您的 build.gradle.kts 依赖项列表中添加以下内容

build.gradle.kts
implementation("org.mongodb:bson-kotlinx:5.2.1")

如果您使用Maven来管理依赖项,请将以下内容添加到您的pom.xml依赖列表中

pom.xml
<dependency>
<groupId>org.jetbrains.kotlinx</groupId>
<artifactId>bson-kotlinx</artifactId>
<version>5.2.1</version>
</dependency>

注意

bson-kotlin 依赖项

您还可以选择通过默认编解码器注册表安装bson-kotlin依赖项。此依赖项使用反射和编解码器注册表来支持Kotlin数据类,但不支持某些POJO注释,如BsonDiscriminatorBsonExtraElementsBsonConstructor。有关更多信息,请参阅bson-kotlin API文档

通常,我们建议您安装并使用更快的bson-kotlinx库进行编解码器配置。

然后,您可以使用 KotlinSerializerCodec.create() 方法定义您的编解码器,并将其添加到注册表中。

以下示例展示了如何使用 KotlinSerializerCodec.create() 方法创建编解码器,并将其配置为不编码默认值

import org.bson.codecs.configuration.CodecRegistries
import org.bson.codecs.kotlinx.BsonConfiguration
import org.bson.codecs.kotlinx.KotlinSerializerCodec
val myCustomCodec = KotlinSerializerCodec.create<PaintOrder>(
bsonConfiguration = BsonConfiguration(encodeDefaults = false)
)
val registry = CodecRegistries.fromRegistries(
CodecRegistries.fromCodecs(myCustomCodec), collection.codecRegistry
)

有关本节中提到的方法和类的更多信息,请参阅以下API文档

Kotlin 驱动程序原生支持多态类的序列化和反序列化。当您使用 @Serializable 注解标记密封接口及其继承该接口的数据类时,驱动程序使用 KSerializer 实现来处理您的类型与 BSON 之间的转换。

当您将多态数据类的一个实例插入MongoDB时,驱动程序会添加字段_t,即识别字段。该字段的值是数据类的名称。

以下示例创建了一个接口和两个继承该接口的数据类。在数据类中,id字段标记了标注数据类部分中描述的注解。

@Serializable
sealed interface Person {
val name: String
}
@Serializable
data class Student(
@Contextual
@SerialName("_id")
val id: ObjectId,
override val name: String,
val grade: Int,
) : Person
@Serializable
data class Teacher(
@Contextual
@SerialName("_id")
val id: ObjectId,
override val name: String,
val department: String,
) : Person

然后,您可以像往常一样使用数据类进行操作。以下示例使用Person接口参数化集合,然后对多态类TeacherStudent执行操作。当您检索文档时,驱动程序会自动根据识别字段的值检测类型,并相应地进行反序列化。

val collection = database.getCollection<Person>("school")
val teacherDoc = Teacher(ObjectId(), "Vivian Lee", "History")
val studentDoc = Student(ObjectId(), "Kate Parker", 10)
collection.insertOne(teacherDoc)
collection.insertOne(studentDoc)
println("Retrieving by using data classes")
collection.withDocumentClass<Teacher>()
.find(Filters.exists("department"))
.first().also { println(it) }
collection.withDocumentClass<Student>()
.find(Filters.exists("grade"))
.first().also { println(it) }
println("\nRetrieving by using Person interface")
val resultsFlow = collection.withDocumentClass<Person>().find()
resultsFlow.collect { println(it) }
println("\nRetrieving as Document type")
val resultsDocFlow = collection.withDocumentClass<Document>().find()
resultsDocFlow.collect { println(it) }
Retrieving by using data classes
Teacher(id=..., name=Vivian Lee, department=History)
Student(id=..., name=Kate Parker, grade=10)
Retrieving by using Person interface
Teacher(id=..., name=Vivian Lee, department=History)
Student(id=..., name=Kate Parker, grade=10)
Retrieving as Document type
Document{{_id=..., _t=Teacher, name=Vivian Lee, department=History}}
Document{{_id=..., _t=Student, name=Kate Parker, grade=10}}

在本节中,您可以了解如何使用Kotlin序列化处理日期和时间类型。

kotlinx-datetime 是一个 Kotlin 库,它提供了对如何序列化日期和时间值的较高控制。要使用此库,请将 kotlinx-datetime 依赖项添加到您的项目依赖项列表中。

选择以下选项卡,了解如何使用 GradleMaven 软件包管理器将 kotlinx-datetime 依赖项添加到您的项目中

build.gradle.kts
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.6.1")
pom.xml
<dependency>
<groupId>org.jetbrains.kotlinx</groupId>
<artifactId>kotlinx-datetime-jvm</artifactId>
<version>0.6.1</version>
</dependency>

有关此库的更多信息,请参阅 GitHub 上的 kotlinx-datetime 仓库

添加库依赖项后,您可以使用来自 kotlinx-datetime 库的序列化程序,将数据类字段值映射到预期的 BSON 类型。

在此示例中,驱动程序使用以下行为序列化 Appointment 数据类的字段

  • name:驱动程序将值序列化为字符串。

  • date:驱动程序使用 kotlinx-datetime 序列化程序,因为该字段具有 @Contextual 注解。将 LocalDate 值序列化为 BSON 日期。

  • 时间:因为驱动器没有使用@Contextual注解,所以它将值序列化为字符串。这是LocalTime值的默认序列化行为。

@Serializable
data class Appointment(
val name: String,
@Contextual val date: LocalDate,
val time: LocalTime,
)

以下示例将Appointment数据类的实例插入到appointments集合中

val collection = database.getCollection<Appointment>("appointments")
val apptDoc = Appointment(
"Daria Smith",
LocalDate(2024, 10, 15),
LocalTime(hour = 11, minute = 30)
)
collection.insertOne(apptDoc)

在MongoDB中,LocalDate值被存储为BSON日期,而time字段默认通过序列化存储为字符串

{
"_id": ...,
"name": "Daria Smith",
"date": {
"$date": "2024-10-15T00:00:00.000Z"
},
"time": "11:30",
}

返回

文档