Docs 菜单
Docs 主页
/ / /
Kotlin Sync 驱动程序
/

编解码器

在此页面上

  • Overview
  • 编解码器
  • encode() 方法
  • Decide() 方法
  • 示例
  • CodecRegistry
  • CodecProvider
  • 默认编解码器注册表
  • BsonTypeClassMap
  • 自定义编解码器示例

在本指南中,您可以学习;了解编解码器和支持类,它们处理Kotlin对象与BSON数据之间的编码和解码。 Codec抽象类允许您将任何Kotlin类型映射到相应的BSON类型。 您可以使用此功能将域对象直接映射到 BSON,或从BSON直接映射到域对象,而无需使用DocumentBsonDocument等类。

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

  • 编解码器

  • CodecRegistry

  • CodecProvider

  • 自定义编解码器示例

提示

Kotlin 序列化

您可以使用Kotlin序列化来通过@Serializable类处理数据编码和解码,而不是实施自定义编解码器。 如果您已经熟悉kotlinx.serialization库或更喜欢使用一致的Kotlin方法,则可以选择Kotlin序列化。 要学习;了解详情,请参阅 Kotlin序列化指南。

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

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

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

Parameter Type
说明
writer
实施 BsonWriter 的类的实例,该接口类型公开用于编写 BSON 文档的方法。例如,BsonBinaryWriter 实施写入二进制数据流。使用此实例通过适当的写入方法写入 BSON 值。
value
实施所编码的数据。该类型必须与分配给您的实施的类型变量相匹配。
encoderContext
包含有关编码为BSON的Kotlin对象数据的元信息,包括是否将当前值存储在MongoDB集合中。

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

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是对Kotlin类进行编码和解码的Codec实例的不可变集合。 您可以使用以下任何CodecRegistries类静态工厂方法从关联类型中包含的Codec实例构造CodecRegistry

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

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

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

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

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

前面的示例为CodecRegistry分配了以下Codec实现:

  • IntegerCodec:用于转换CodecIntegers 。它是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类的行为,但保留其他类的默认编解码器注册表中的行为,您可以按优先顺序指定注册表。 示例,假设您想使用自定义MyEnumCodec覆盖枚举类型的Codec默认提供商行为。 您必须将其添加到注册表列表中默认编解码器注册表之前的位置,如以下示例所示:

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

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

BsonTypeClassMap类包含BSON和Kotlin类型之间的推荐映射。 您可以在自定义Codec 或 中使用此类来帮助您管理将CodecProvider Kotlin解码为哪些BSON types 类型。它还包含实现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构造函数中指定替换项来修改实例中的这些映射。 以下代码片段展示了如何将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文档。

本部分演示如何实现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"状态。 对于此字段,使用PowerStatusCodecPowerStatus枚举值转换为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 {
@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
}
}

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

  • 在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 文档:

后退

Kotlin 序列化