Docs Menu
Docs Home
/ /

Codificar datos con códecs de tipo

En esta guía, puede aprender sobre los códecs y las clases de soporte que manejan la codificación y decodificación de objetos Kotlin hacia y desde datos BSON. Codec La abstracción permite mapear cualquier tipo de Kotlin a su tipo BSON correspondiente. Puede usar esta función para mapear los objetos de su dominio directamente hacia y desde BSON en lugar de usar clases como Document o BsonDocument.

Puede aprender a especificar la lógica de codificación y decodificación personalizada utilizando la abstracción Codec y ver implementaciones de ejemplo en las siguientes secciones:

  • Códec

  • Registro de códecs

  • Proveedor de códecs

  • Ejemplo de códec personalizado

Tip

Serialización de Kotlin

Puede usar la serialización de Kotlin para gestionar la codificación y decodificación de datos con clases @Serializable en lugar de implementar códecs personalizados. Puede optar por la serialización de Kotlin si ya está familiarizado con la biblioteca kotlinx.serialization o si prefiere usar un enfoque idiomático de Kotlin. Para obtener más información, consulte Guíade serialización de datos.

La interfaz Codec contiene métodos abstractos para serializar y deserializar objetos Kotlin a datos BSON. Puedes definir una lógica de conversión personalizada en tu implementación de esta interfaz.

Para implementar la interfaz Codec, anule los métodos abstractos encode(), decode() y getEncoderClass().

El método encode() requiere los siguientes parámetros:

Parameter Type
Descripción

writer

Una instancia de una clase que implementa BsonWriter, un tipo de interfaz que expone métodos para escribir un documento BSON. Por ejemplo, la implementación BsonBinaryWriter escribe en un flujo binario de datos. Utilice esta instancia para escribir su valor BSON con el método de escritura adecuado.

value

Los datos que codifica su implementación. El tipo debe coincidir con la variable de tipo asignada a su implementación.

encoderContext

Contiene metainformación sobre los datos del objeto Kotlin que codifica en BSON, incluido si se debe almacenar el valor actual en una colección MongoDB.

El método encode() utiliza la instancia BsonWriter para enviar el valor codificado a MongoDB y no devuelve un valor.

El método decode() devuelve la instancia del objeto Kotlin rellenada con el valor de los datos BSON. Este método requiere los siguientes parámetros:

Parameter Type
Descripción

reader

Una instancia de una clase que implementa BsonReader, un tipo de interfaz que expone métodos para leer un documento BSON. Por ejemplo, la implementación BsonBinaryReader lee de un flujo binario de datos.

decoderContext

Contiene información sobre los datos BSON que decodifica en un objeto Kotlin.

El método getEncoderClass() devuelve una instancia de clase de la clase Kotlin, ya que Kotlin no puede inferir el tipo debido a la eliminación de tipo.

Esta sección contiene ejemplos de código que muestran cómo implementar una interfaz Codec personalizada.

La enumeración PowerStatus contiene los valores "ON" y "OFF" para representar los estados de un interruptor eléctrico:

enum class PowerStatus {
ON,
OFF
}

La clase PowerStatusCodec implementa la interfaz Codec para convertir los valores enum de Kotlin en los valores booleanos BSON correspondientes. El método encode() convierte un valor PowerStatus en un booleano BSON y el método decode() realiza la conversión en sentido inverso.

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
}

Puedes agregar una instancia de PowerStatusCodec a CodecRegistry tu. Consulta la sección Registro de Códecs de esta página para saber cómo incluir tu Codec en tu registro.

Para obtener más información sobre las clases e interfaces mencionadas en esta sección, consulte la siguiente documentación de API:

Un CodecRegistry es una colección inmutable de instancias Codec que codifican y decodifican clases Kotlin. Puede usar cualquiera de los siguientes métodos de fábrica estáticos de clase CodecRegistries para construir un CodecRegistry a partir de las instancias Codec contenidas en los tipos asociados:

  • fromCodecs():Crea un registro a partir de Codec instancias

  • fromProviders():Crea un registro a partir de CodecProvider instancias

  • fromRegistries():Crea un registro a partir de CodecRegistry instancias

El siguiente código muestra cómo construir un CodecRegistry utilizando el método fromCodecs():

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

El ejemplo anterior asigna a CodecRegistry las siguientes implementaciones Codec:

  • IntegerCodec: Codec que convierte Integers. Forma parte del paquete BSON.

  • PowerStatusCodec:Muestra Codec de la sección anterior que convierte valores de enumeración de Kotlin en booleanos BSON.

Puede recuperar las instancias Codec de la instancia CodecRegistry utilizando el siguiente código:

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

Si intenta recuperar una instancia Codec para una clase que no está registrada, el método codecRegistry.get() genera una excepción CodecConfigurationException.

Para obtener más información sobre las clases e interfaces de esta sección, consulte la siguiente documentación de API:

Una interfaz CodecProvider contiene métodos abstractos para crear instancias Codec y asignarlas a instancias CodecRegistry. Al igual que la interfaz CodecRegistry, la biblioteca BSON utiliza las instancias Codec recuperadas por el método CodecProvider.get() para convertir entre los tipos de datos Kotlin y BSON.

Sin embargo, si agrega una clase que contiene campos que requieren objetos Codec correspondientes, asegúrese de instanciar los objetos Codec para los campos de la clase antes de instanciar los objetos Codec para toda la clase. Puede usar el parámetro CodecRegistry en el método CodecProvider.get() para pasar cualquiera de las instancias Codec de las que depende el objeto Codec.

Para ver un ejemplo ejecutable que demuestre operaciones de lectura y escritura utilizando Codec clases, consulte la sección Ejemplo de códec personalizado de esta guía.

El registro de códecs predeterminado es un conjunto de clases CodecProvider que especifican la conversión entre objetos Kotlin de uso común y tipos MongoDB. El controlador utiliza automáticamente el registro de códecs predeterminado a menos que se especifique uno diferente.

Para anular el comportamiento de una o más clases Codec, pero conservar el comportamiento del registro de códecs predeterminado para las demás clases, puede especificar los registros en orden de precedencia. Por ejemplo, supongamos que desea anular el comportamiento predeterminado del proveedor de un Codec para tipos de enumeración con su MyEnumCodec personalizado. Debe añadirlo a la lista de registros antes del registro de códecs predeterminado, como se muestra en el siguiente ejemplo:

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

Para obtener más información sobre las clases e interfaces de esta sección, consulte la siguiente documentación de API:

La clase BsonTypeClassMap contiene una asignación recomendada entre los tipos BSON y Kotlin. Puedes usar esta clase en tus Codec o CodecProvider personalizados para gestionar los tipos Kotlin a los que decodificar tus tipos BSON. También contiene clases contenedoras que implementan Iterable o Map, como la clase Document.

Puede agregar o modificar la asignación BsonTypeClassMap por defecto pasando un Map que contenga nuevas entradas o reemplazos.

El siguiente código muestra cómo recuperar el tipo de clase Kotlin que corresponde al tipo de matriz BSON en la instancia predeterminada BsonTypeClassMap:

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

Puede modificar estas asignaciones en su instancia especificando reemplazos en el constructor BsonTypeClassMap. El siguiente fragmento de código muestra cómo reemplazar la asignación del tipo de matriz BSON en su instancia BsonTypeClassMap con la clase 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

Para obtener una lista completa de las asignaciones predeterminadas, consulte la documentación de la API BsonTypeClassMap.

Esta sección muestra cómo implementar las interfaces Codec y CodecProvider para definir la lógica de codificación y decodificación de una clase Kotlin personalizada. También muestra cómo especificar y usar sus implementaciones personalizadas para realizar operaciones de lectura y escritura.

El siguiente código define la clase de datos de muestra Monolight:

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

Esta clase contiene los siguientes campos, cada uno de los cuales requiere un Codec correspondiente para manejar la codificación y decodificación:

  • powerStatus: Describe si la luz del dispositivo está "ON" o "OFF". Para este campo, use la PowerStatusCodec que convierte los valores de la enumeración PowerStatus en booleanos BSON.

  • colorTemperatureDescribe el color de la luz del dispositivo en kelvins como un valor Int. Para este campo, utilice el valor IntegerCodec proporcionado en la biblioteca BSON.

El siguiente código muestra cómo implementar un Codec para la clase Monolight. El constructor espera una instancia de CodecRegistry, de la cual recupera las instancias Codec necesarias para codificar y decodificar los campos de la clase:

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
}

Para garantizar que las instancias Codec de los campos estén disponibles para la clase Monolight, implemente un CodecProvider personalizado como se muestra en el siguiente ejemplo de código:

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
}
}

Después de definir la lógica de conversión, puedes realizar las siguientes acciones:

  • Almacenar instancias de Monolight en MongoDB

  • Recuperar documentos de MongoDB como instancias de Monolight

El siguiente código asigna MonolightCodecProvider a la instancia MongoCollection pasándola al método withCodecRegistry(). La clase de ejemplo también inserta y recupera datos mediante la clase 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 }

Para obtener más información sobre los métodos y clases mencionados en esta sección, consulte la siguiente documentación de API:

Volver

Serialización

En esta página