对于 AI 代理:可在 https://www.mongodb.com/zh-cn/docs/llms.txt 获取文档索引—通过在任何 URL 路径后添加 .md 可获取所有页面的 Markdown 版本。
Docs 菜单

使用类型编解码器对数据进行编码

在本指南中,您可以了解编解码器以及支持类,它们在 MongoDB Kotlin 驱动程序中处理 Kotlin 对象与 BSON 数据的编码和解码。 Codec抽象类允许您将任何 Kotlin 类型映射到相应的 BSON 类型。 您可以使用它将域对象直接映射到 BSON 或从 BSON 直接映射到域对象,而无需使用数据类或基于中间映射的对象,例如DocumentBsonDocument

可以了解如何使用 Codec 抽象类来指定自定义编码和解码逻辑,并在以下部分中查看示例实施:

Codec接口包含用于将 Kotlin 对象序列化和反序列化为 BSON 数据的抽象方法。 您可以在该接口的实现中定义 BSON 和 Kotlin 对象之间的转换逻辑。

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

encode() 方法需要使用以下参数:

Parameter Type
说明

writer

实施 BsonWriter 的类的实例,该接口类型公开用于编写 BSON 文档的方法。例如,BsonBinaryWriter 实施写入二进制数据流。使用此实例通过适当的写入方法写入 BSON 值。

value

实施所编码的数据。该类型必须与分配给您的实施的类型变量相匹配。

encoderContext

包含有关编码为 BSON 的 Kotlin 对象数据的元信息,包括是否将当前值存储在 MongoDB 集合中。

此方法使用 BsonWriter 实例将编码值发送到 MongoDB,并且不返回值。

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

Parameter Type
说明

bsonReader

实现 BsonReader 的类的实例,该接口类型公开用于读取 BSON 文档的方法。例如,BsonBinaryReader 实施从二进制数据流中读取。

decoderContext

包含有关解码为 Kotlin 对象的 BSON 数据的信息。

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

请参阅以下代码示例,这些示例展示了如何实施自定义 Codec

PowerStatus 枚举包含值“打开”和“关闭”来表示电气开关的状态。

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中,其中包含Codec与其适用的 Kotlin 对象类型之间的映射。 继续访问本页的CodecRegistry部分,查看如何包含Codec

有关此部分中的类和接口的更多信息,请参阅以下 API 文档:

CodecRegistryCodec 实例的不可变集合,这些实例对其指定的 Kotlin 类进行编码和解码。您可以使用以下任一 CodecRegistries 类静态工厂方法,从关联类型中包含的 Codec 实例构造 CodecRegistry

  • fromCodecs()

  • fromProviders()

  • fromRegistries()

以下代码片段演示如何使用 fromCodecs() 方法构造 CodecRegistry

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

在前面的示例中,我们为 CodecRegistry 分配以下 Codec 实施:

  • IntegerCodec,一个用于转换 IntegersCodec,并且是 BSON 软件包的一部分。

  • PowerStatusCodec ,我们的示例Codec ,它将 Kotlin 枚举值转换为 BSON 布尔值。

您可以使用以下代码从上一示例中的 CodecRegistry 实例中检索 Codec 实例:

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

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

有关此部分中的类和接口的更多信息,请参阅以下 API 文档:

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

但是,如果您添加的类包含需要相应Codec对象的字段,则需要确保在实例化该类的Codec之前为该类的字段实例化Codec对象。 您可以使用get()方法中的CodecRegistry参数来传递Codec依赖的任何Codec实例。

以下代码示例展示了如何实现 CodecProvider 以将其在CodecRegistry 实例(例如前面示例中的 PowerStatusCodec)所需的任何 Codec 实例传递给MonolightCodec

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 类进行读写操作的可运行示例,请参阅本指南的自定义编解码器示例一节。

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

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

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

有关此部分中的类和接口的更多信息,请参阅以下 API 文档部分:

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

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

以下代码片段展示了如何检索与默认BsonTypeClassMap实例中的 BSON 类型相对应的 Kotlin 类类型:

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

可以通过在 BsonTypeClassMap 构造函数中指定替换项来修改实例中的这些映射。以下代码段展示了如何将 BsonTypeClassMap 实例中的 ARRAY 的映射替换为 Set 类:

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)
Java type: java.util.Set

请参阅 bsonTy peClassMap API 文档,获取默认映射的完整列表。

有关 Document 类如何使用 BsonTypeClassMap 的示例,请参阅以下类的驱动程序源代码:

在本部分中,我们将展示如何实现 CodecCodecProvider 来定义自定义 Kotlin 类的编码和解码逻辑。我们还展示了如何指定和使用自定义实现来执行插入和检索操作。

提示

Kotlin 序列化

作为实现自定义编解码器的替代方法,您可以使用 Kotlin 序列化,通过@Serializable类处理数据编码和解码。 如果您已经熟悉该框架或更喜欢使用惯用的 Kotlin 方法,则可以选择 Kotlin 序列化。 有关更多信息,请参阅Kotlin 序列化文档。

以下代码片段显示名为 Monolight 的示例自定义类以及我们要在 MongoDB 集合中存储和检索的字段:

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

此类包含以下字段,我们需要为每个字段分配一个 Codec

  • powerStatus 描述灯是“开”还是“关”,为此我们使用 PowerStatusCodec 将特定枚举值转换为 BSON 布尔值。

  • colorTemperature 描述灯光的颜色并包含一个 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 类和关联的编解码器插入和检索数据:

fun main() = runBlocking {
val mongoClient = MongoClient.create("<connection string uri>")
val codecRegistry = CodecRegistries.fromRegistries(
CodecRegistries.fromCodecs(IntegerCodec(), PowerStatusCodec()),
CodecRegistries.fromProviders(MonolightCodecProvider()),
MongoClientSettings.getDefaultCodecRegistry()
)
val database = mongoClient.getDatabase("codecs_example_products")
val collection = database.getCollection<Monolight>("monolights")
.withCodecRegistry(codecRegistry)
// Construct and insert an instance of Monolight
val myMonolight = Monolight(PowerStatus.ON, 5200)
collection.insertOne(myMonolight)
// Retrieve one or more instances of Monolight
val lights = collection.find().toList()
println(lights)
}
[Monolight [powerStatus=ON, colorTemperature=5200]]

有关本节中提到的方法和类的详情,请参阅以下 API 文档: