Overview
在本指南中,您可以学习;了解编解码器和支持类,它们处理Kotlin对象与BSON数据之间的编码和解码。 Codec抽象类允许您将任何Kotlin类型映射到相应的BSON类型。 您可以使用此功能将域对象直接映射到 BSON,或从BSON直接映射到域对象,而无需使用Document或BsonDocument等类。
可以了解如何使用 Codec 抽象类来指定自定义编码和解码逻辑,并在以下部分中查看示例实施:
提示
Kotlin 序列化
您可以使用Kotlin序列化来通过 @Serializable 类处理数据编码和解码,而不是实施自定义编解码器。如果您已经熟悉 kotlinx.serialization 库或更喜欢使用一致的Kotlin方法,则可以选择Kotlin序列化。要学习;了解详情,请参阅 数据序列化指南。
编解码器
Codec接口包含用于将Kotlin对象序列化和反序列化为BSON数据的抽象方法。 您可以在该接口的实施中定义自定义转换逻辑。
要实现 Codec 接口,请覆盖 encode()、 decode() 和 getEncoderClass() 抽象方法。
encode() 方法
encode() 方法需要使用以下参数:
Parameter Type | 说明 |
|---|---|
| 实施 |
| 实施所编码的数据。该类型必须与分配给您的实施的类型变量相匹配。 |
| 包含有关编码为BSON的Kotlin对象数据的元信息,包括是否将当前值存储在MongoDB集合中。 |
encode()方法使用BsonWriter实例将编码值发送到MongoDB ,并且不返回值。
Decide() 方法
decode()方法返回用 BSON 数据中的值填充的 Kotlin 对象实例。 此方法需要以下参数:
Parameter Type | 说明 |
|---|---|
| 实现 |
| 包含有关解码为 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。
以下代码展示了如何为 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 } }
定义转换逻辑后,您可以执行以下操作:
在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 文档: