文档菜单
文档首页
/ / /
Kotlin 同步驱动程序
/

编解码器

在本页中

  • 概述
  • 编解码器
  • encode() 方法
  • decode() 方法
  • 示例
  • 编解码器注册表
  • 编解码器提供者
  • 默认编解码器注册表
  • BsonTypeClassMap
  • 自定义编解码器示例

在本指南中,您可以了解 编解码器 以及支持类,这些类处理 Kotlin 对象与 BSON 数据之间的编码和解码。TheCodec 抽象允许您将任何 Kotlin 类型映射到相应的 BSON 类型。您可以使用此功能将域对象直接映射到 BSON,而不必使用 DocumentBsonDocument 等类。

您可以在以下部分学习如何使用 Codec 抽象指定自定义编码和解码逻辑,并查看示例实现。

  • 编解码器

  • 编解码器注册表

  • 编解码器提供者

  • 自定义编解码器示例

提示

Kotlin 序列化

您可以使用 Kotlin 序列化来处理数据编码和解码,使用带有 @Serializable 类的 Kotlin 序列化,而不是实现自定义编解码器。如果您已经熟悉 kotlinx.serialization 库或更喜欢使用 Kotlin 语法,则可能选择 Kotlin 序列化。有关更多信息,请参阅Kotlin 序列化 指南。

Codec 接口包含将 Kotlin 对象序列化和反序列化为 BSON 数据的抽象方法。您可以在实现此接口时定义自定义转换逻辑。

要实现 Codec 接口,需要覆盖 encode()decode()getEncoderClass() 抽象方法。

encode() 方法需要以下参数

参数类型
描述
writer
实现 BsonWriter 接口的类的实例,这是一个公开写入 BSON 文档方法的接口类型。例如,BsonBinaryWriter 实现将数据写入二进制数据流。使用此实例使用适当的写入方法写入您的 BSON 值。
value
您的实现编码的数据。类型必须与分配给您的实现的类型变量相匹配。
encoderContext
包含编码到BSON中Kotlin对象数据的元信息,包括是否将当前值存储在MongoDB集合中。

使用encode()方法,通过BsonWriter实例将编码后的值发送到MongoDB,并且不返回任何值。

decode()方法返回一个填充了BSON数据值的Kotlin对象实例。此方法需要以下参数:

参数类型
描述
reader
实现BsonReader接口的类的实例,这是一个公开读取BSON文档的方法的接口类型。例如,BsonBinaryReader实现从数据二进制流中读取。
decoderContext
包含解码到Kotlin对象中的BSON数据的信息。

getEncoderClass()方法返回Kotlin类的类实例,因为Kotlin无法推断类型,这是由于类型擦除。

本节包含代码示例,展示了如何实现自定义的Codec接口。

PowerStatus枚举包含值"ON""OFF",用于表示电开关的状态。

enum class PowerStatus {
ON,
OFF
}

PowerStatusCodec类实现了Codec接口,将Kotlin enum值转换为相应的BSON布尔值。encode()方法将PowerStatus值转换为BSON布尔值,而decode()方法执行相反方向的转换。

class PowerStatusCodec : Codec<PowerStatus> {
override fun encode(
writer: BsonWriter, value: PowerStatus, encoderContext: EncoderContext
) = writer.writeBoolean(value == PowerStatus.ON)
override fun decode(
reader: BsonReader, decoderContext: DecoderContext)
: PowerStatus {
return when (reader.readBoolean()) {
true -> PowerStatus.ON
false -> PowerStatus.OFF
}
}
override fun getEncoderClass(): Class<PowerStatus> = PowerStatus::class.java
}

您可以将一个 PowerStatusCodec 实例添加到您的 CodecRegistry 中。查看本页面的 CodecRegistry 部分,了解如何将您的 Codec 包含在注册表中。

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

CodecRegistry 是一个不可变的 Codec 实例集合,用于编码和解码 Kotlin 类。您可以使用以下任何 CodecRegistries 类静态工厂方法,从关联类型中包含的 Codec 实例构建 CodecRegistry

  • fromCodecs():从 Codec 实例创建注册表

  • fromProviders():从 CodecProvider 实例创建注册表

  • fromRegistries():从 CodecRegistry 实例创建注册表

以下代码显示了如何使用 fromCodecs() 方法构建 CodecRegistry

val codecRegistry = CodecRegistries
.fromCodecs(IntegerCodec(), PowerStatusCodec())

前面的示例将以下 Codec 实现分配给 CodecRegistry

  • IntegerCodec:将 Integers 转换的 Codec。它是 BSON 包的一部分。

  • PowerStatusCodec:来自上一节的示例 Codec,将 Kotlin 枚举值转换为 BSON 布尔值。

您可以使用以下代码从 CodecRegistry 实例检索 Codec 实例

val powerStatusCodec = codecRegistry.get(PowerStatus::class.java)
val integerCodec = codecRegistry.get(Integer::class.java)

如果您尝试检索未注册类的 Codec 实例,则 codecRegistry.get() 方法会引发 CodecConfigurationException 异常。

有关本节中类和接口的更多信息,请参阅以下 API 文档

CodecProvider 是一个接口,包含创建 Codec 实例并将其分配给 CodecRegistry 实例的抽象方法。类似于 CodecRegistry 接口,BSON 库使用 CodecProvider.get() 方法获取的 Codec 实例在 Kotlin 和 BSON 数据类型之间进行转换。

然而,在您添加包含需要相应 Codec 对象的字段的类的情况下,确保在为整个类创建 Codec 之前,为类的字段实例化 Codec 对象。您可以使用 CodecProvider.get() 方法中的 CodecRegistry 参数传递 Codec 依赖的任何 Codec 实例。

要查看使用 Codec 类进行读写操作的示例,请参阅本指南中的 自定义编解码器示例 部分。

默认编解码器注册表是一组 CodecProvider 类,用于指定常用 Kotlin 对象和 MongoDB 类型之间的转换。除非您指定了不同的注册表,否则驱动程序将自动使用默认编解码器注册表。

要覆盖一个或多个 Codec 类的行为,但保留其他类的默认编解码器注册表的行为,您可以按优先级顺序指定注册表。例如,假设您想覆盖默认提供程序对枚举类型的 Codec 行为,使用您自定义的 MyEnumCodec。您必须将其添加到注册表列表中,位置在默认编解码器注册表之前,如下例所示

val newRegistry = CodecRegistries.fromRegistries(
CodecRegistries.fromCodecs(MyEnumCodec()),
MongoClientSettings.getCodecRegistry()
)

有关本节中类和接口的更多信息,请参阅以下 API 文档

BsonTypeClassMap 类包含 BSON 和 Kotlin 类型之间推荐的映射。您可以在自定义 CodecCodecProvider 中使用此类来帮助您管理将哪个 Kotlin 类型解码为您的 BSON 类型。它还包含实现 IterableMap 的容器类,例如 Document 类。

您可以通过传递包含新条目或替换条目的 Map 来添加或修改 BsonTypeClassMap 的默认映射。

以下代码演示了如何在默认的 BsonTypeClassMap 实例中检索与 BSON 数组类型对应的 Kotlin 类类型

val bsonTypeClassMap = BsonTypeClassMap()
val clazz = bsonTypeClassMap[BsonType.ARRAY]
println("Kotlin class name: " + clazz.name)
Kotlin class name: java.util.List

您可以通过在 BsonTypeClassMap 构造函数中指定替换来修改这些映射。以下代码片段显示了如何用 Set 类替换您的 BsonTypeClassMap 实例中 BSON 数组类型的映射

val replacements = mutableMapOf<BsonType, Class<*>>(BsonType.ARRAY to MutableSet::class.java)
val bsonTypeClassMap = BsonTypeClassMap(replacements)
val clazz = bsonTypeClassMap[BsonType.ARRAY]
println("Class name: " + clazz.name)
Kotlin class name: java.util.Set

要查看默认映射的完整列表,请参阅 BsonTypeClassMap API 文档。

本节演示了如何实现 CodecCodecProvider 接口来定义自定义 Kotlin 类的编码和解码逻辑。它展示了如何指定和使用自定义实现以执行读写操作。

以下代码定义了示例数据类 Monolight

data class Monolight(
var powerStatus: PowerStatus = PowerStatus.OFF,
var colorTemperature: Int? = null
) {
override fun toString():
String = "Monolight { powerStatus: $powerStatus, colorTemperature: $colorTemperature }"
}

该类包含以下字段,每个字段都需要相应的 Codec 来处理编码和解码

  • powerStatus:描述设备灯光是 "ON" 还是 "OFF"。对于此字段,使用 PowerStatusCodec,它将 PowerStatus 枚举值转换为 BSON 布尔值。

  • colorTemperature:以 kelvin 为单位的设备灯光颜色,作为 Int 值。对于此字段,使用 BSON 库中提供的 IntegerCodec

以下代码展示了如何为 Monolight 类实现 Codec。构造函数期望一个 CodecRegistry 实例,从中检索编码和解码类字段所需的 Codec 实例

class MonolightCodec(registry: CodecRegistry) : Codec<Monolight> {
private val powerStatusCodec: Codec<PowerStatus>
private val integerCodec: Codec<Int>
init {
powerStatusCodec = registry[PowerStatus::class.java]
integerCodec = IntegerCodec()
}
override fun encode(writer: BsonWriter, value: Monolight, encoderContext: EncoderContext) {
writer.writeStartDocument()
writer.writeName("powerStatus")
powerStatusCodec.encode(writer, value.powerStatus, encoderContext)
writer.writeName("colorTemperature")
integerCodec.encode(writer, value.colorTemperature, encoderContext)
writer.writeEndDocument()
}
override fun decode(reader: BsonReader, decoderContext: DecoderContext): Monolight {
val monolight = Monolight()
reader.readStartDocument()
while (reader.readBsonType() != BsonType.END_OF_DOCUMENT) {
when (reader.readName()) {
"powerStatus" -> monolight.powerStatus =
powerStatusCodec.decode(reader, decoderContext)
"colorTemperature" -> monolight.colorTemperature =
integerCodec.decode(reader, decoderContext)
"_id" -> reader.readObjectId()
}
}
reader.readEndDocument()
return monolight
}
override fun getEncoderClass(): Class<Monolight> = Monolight::class.java
}

为了确保字段 Codec 实例对 Monolight 类可用,实现一个自定义的 CodecProvider,如下面的代码示例所示

class MonolightCodecProvider : CodecProvider {
@Suppress("UNCHECKED_CAST")
override fun <T> get(clazz: Class<T>, registry: CodecRegistry): Codec<T>? {
return if (clazz == Monolight::class.java) {
MonolightCodec(registry) as Codec<T>
} else null // Return null when not a provider for the requested class
}
}

在定义转换逻辑后,您可以执行以下操作

  • Monolight 实例存储在 MongoDB 中

  • 将 MongoDB 中的文档检索为 Monolight 实例

以下代码通过将 MonolightCodecProvider 传递给 withCodecRegistry() 方法,将其分配给 MongoCollection 实例。示例类还通过使用 Monolight 类来插入和检索数据

val mongoClient = MongoClient.create(uri)
val codecRegistry = CodecRegistries.fromRegistries(
CodecRegistries.fromCodecs(IntegerCodec(), PowerStatusCodec()),
CodecRegistries.fromProviders(MonolightCodecProvider()),
MongoClientSettings.getDefaultCodecRegistry()
)
val database = mongoClient.getDatabase("codec_test")
val collection = database.getCollection<Monolight>("monolights")
.withCodecRegistry(codecRegistry)
// Insert instances of Monolight
val monolights = listOf(
Monolight(PowerStatus.ON, 5200),
Monolight(PowerStatus.OFF, 3000)
)
collection.insertMany(monolights)
// Retrieve instances of Monolight
val results = collection.find()
results.forEach { l ->
println(l)
}
Monolight { powerStatus: ON, colorTemperature: 5200 }
Monolight { powerStatus: OFF, colorTemperature: 3000 }

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

返回

Kotlin 序列化