编解码器
在此页面上
Overview
在本指南中,您可以学习;了解编解码器和支持类,它们处理Kotlin对象与BSON数据之间的编码和解码。 Codec
抽象类允许您将任何Kotlin类型映射到相应的BSON类型。 您可以使用此功能将域对象直接映射到 BSON,或从BSON直接映射到域对象,而无需使用Document
或BsonDocument
等类。
可以了解如何使用 Codec
抽象类来指定自定义编码和解码逻辑,并在以下部分中查看示例实施:
提示
Kotlin 序列化
您可以使用Kotlin序列化来通过@Serializable
类处理数据编码和解码,而不是实施自定义编解码器。 如果您已经熟悉kotlinx.serialization
库或更喜欢使用一致的Kotlin方法,则可以选择Kotlin序列化。 要学习;了解详情,请参阅 Kotlin序列化指南。
编解码器
Codec
接口包含用于将Kotlin对象序列化和反序列化为BSON数据的抽象方法。 您可以在该接口的实施中定义自定义转换逻辑。
要实现 Codec
接口,请覆盖 encode()
、 decode()
和 getEncoderClass()
抽象方法。
encode() 方法
encode()
方法需要使用以下参数:
Parameter Type | 说明 |
---|---|
writer | 实施 BsonWriter 的类的实例,该接口类型公开用于编写 BSON 文档的方法。例如,BsonBinaryWriter 实施写入二进制数据流。使用此实例通过适当的写入方法写入 BSON 值。 |
value | 实施所编码的数据。该类型必须与分配给您的实施的类型变量相匹配。 |
encoderContext | 包含有关编码为BSON的Kotlin对象数据的元信息,包括是否将当前值存储在MongoDB集合中。 |
encode()
方法使用BsonWriter
实例将编码值发送到MongoDB ,并且不返回值。
Decide() 方法
decode()
方法返回用 BSON 数据中的值填充的 Kotlin 对象实例。 此方法需要以下参数:
Parameter Type | 说明 |
---|---|
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
是对Kotlin类进行编码和解码的Codec
实例的不可变集合。 您可以使用以下任何CodecRegistries
类静态工厂方法从关联类型中包含的Codec
实例构造CodecRegistry
:
fromCodecs()
:从Codec
实例创建注册表fromProviders()
:从CodecProvider
实例创建注册表fromRegistries()
:从CodecRegistry
实例创建注册表
以下代码展示了如何使用fromCodecs()
方法构造CodecRegistry
:
val codecRegistry = CodecRegistries .fromCodecs(IntegerCodec(), PowerStatusCodec())
前面的示例为CodecRegistry
分配了以下Codec
实现:
IntegerCodec
:用于转换Codec
的Integers
。它是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
CodecProvider
是一个接口,其中包含用于创建Codec
实例并将其分配给CodecRegistry
实例的抽象方法。 与CodecRegistry
接口类似, BSON库使用CodecProvider.get()
方法检索的Codec
实例在Kotlin和BSON数据类型之间进行转换。
但是,如果您添加的类包含需要相应Codec
对象的字段,请确保在实例化整个类的Codec
之前,先实例化该类字段的Codec
对象。 您可以使用CodecProvider.get()
方法中的CodecRegistry
参数来传递Codec
依赖的任何Codec
实例。
要查看演示使用Codec
类进行写入操作的可运行示例,请参阅本指南的“自定义编解码器示例”部分。
默认编解码器注册表
默认编解码器注册表是一设立CodecProvider
类,用于指定常用Kotlin对象和MongoDB类型之间的转换。 除非您指定其他注册表,否则驾驶员会自动使用默认编解码器注册表。
要覆盖一个或多个Codec
类的行为,但保留其他类的默认编解码器注册表中的行为,您可以按优先顺序指定注册表。 示例,假设您想使用自定义MyEnumCodec
覆盖枚举类型的Codec
默认提供商行为。 您必须将其添加到注册表列表中默认编解码器注册表之前的位置,如以下示例所示:
val newRegistry = CodecRegistries.fromRegistries( CodecRegistries.fromCodecs(MyEnumCodec()), MongoClientSettings.getCodecRegistry() )
有关此部分中的类和接口的更多信息,请参阅以下 API 文档:
BsonTypeClassMap
BsonTypeClassMap
类包含BSON和Kotlin类型之间的推荐映射。 您可以在自定义Codec
或 中使用此类来帮助您管理将CodecProvider
Kotlin解码为哪些BSON types 类型。它还包含实现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
构造函数中指定替换项来修改实例中的这些映射。 以下代码片段展示了如何将BsonTypeClassMap
实例中BSON大量类型的映射替换为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)
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
:将设备灯光的颜色(以开尔文为单位)描述为Int
值。 对于此字段,请使用BSON库中提供的IntegerCodec
。
The following code shows how to implement a Codec
for the Monolight
class. The constructor expects an instance of CodecRegistry
from which it retrieves the Codec
instances needed to encode and decode the class fields:
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 } }
定义转换逻辑后,您可以执行以下操作:
在MongoDB中存储
Monolight
的实例从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 文档: