编解码器
概述
在本指南中,您可以了解 编解码器 以及支持类,这些类处理 Kotlin 对象与 BSON 数据之间的编码和解码。TheCodec
抽象允许您将任何 Kotlin 类型映射到相应的 BSON 类型。您可以使用此功能将域对象直接映射到 BSON,而不必使用 Document
或 BsonDocument
等类。
您可以在以下部分学习如何使用 Codec
抽象指定自定义编码和解码逻辑,并查看示例实现。
提示
Kotlin 序列化
您可以使用 Kotlin 序列化来处理数据编码和解码,使用带有 @Serializable
类的 Kotlin 序列化,而不是实现自定义编解码器。如果您已经熟悉 kotlinx.serialization
库或更喜欢使用 Kotlin 语法,则可能选择 Kotlin 序列化。有关更多信息,请参阅Kotlin 序列化 指南。
编解码器
Codec
接口包含将 Kotlin 对象序列化和反序列化为 BSON 数据的抽象方法。您可以在实现此接口时定义自定义转换逻辑。
要实现 Codec
接口,需要覆盖 encode()
、decode()
和 getEncoderClass()
抽象方法。
encode()
方法
encode()
方法需要以下参数
参数类型 | 描述 |
---|---|
writer | 实现 BsonWriter 接口的类的实例,这是一个公开写入 BSON 文档方法的接口类型。例如,BsonBinaryWriter 实现将数据写入二进制数据流。使用此实例使用适当的写入方法写入您的 BSON 值。 |
value | 您的实现编码的数据。类型必须与分配给您的实现的类型变量相匹配。 |
encoderContext | 包含编码到BSON中Kotlin对象数据的元信息,包括是否将当前值存储在MongoDB集合中。 |
使用encode()
方法,通过BsonWriter
实例将编码后的值发送到MongoDB,并且不返回任何值。
decode() 方法
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
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
BsonTypeClassMap
类包含 BSON 和 Kotlin 类型之间推荐的映射。您可以在自定义 Codec
或 CodecProvider
中使用此类来帮助您管理将哪个 Kotlin 类型解码为您的 BSON 类型。它还包含实现 Iterable
或 Map
的容器类,例如 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 文档。
自定义编解码器示例
本节演示了如何实现 Codec
和 CodecProvider
接口来定义自定义 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 { 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 文档