Make the MongoDB docs better! We value your opinion. Share your feedback for a chance to win $100.
Click here >
Docs Menu
Docs Home
/ /

Codifica datos con códecs de tipo

En esta guía, puedes aprender sobre Códecs y las clases de apoyo que gestionan la codificación y decodificación de objetos de Kotlin hacia y desde datos BSON en el driver MongoDB Kotlin. El Codec la abstracción te permite mapear cualquier tipo de Kotlin a un tipo BSON correspondiente. Puedes usar esto para mapear directamente tus objetos de dominio hacia y desde BSON en lugar de usar clases de datos o un objeto basado en mapa intermedio como Document o BsonDocument.

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

  • Códec

  • CodecRegistry

  • CodecProvider

  • Ejemplo de Codec Personalizado

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

Para implementar la interfaz Codec, sobreescribe 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 de BsonBinaryWriter escribe en un flujo binario de datos. Utiliza esta instancia para guardar tu valor BSON usando el método de guardar apropiado.

value

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

encoderContext

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

Este método utiliza la instancia BsonWriter para enviar el valor codificado a MongoDB y no devuelve ningún valor.

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

Parameter Type
Descripción

bsonReader

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 de BsonBinaryReader se lee de un flujo binario de datos.

decoderContext

Contiene información sobre los datos BSON que decodifica a 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.

Consulta los siguientes ejemplos de código que muestran cómo puedes implementar un Codec personalizado.

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 Codec para convertir los valores de enum de Kotlin en los valores booleanos BSON correspondientes. El método encode() convierte un PowerStatus en un booleano BSON y el método decode() realiza la conversión en la dirección opuesta.

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 añadir una instancia del PowerStatusCodec a tu CodecRegistry que contiene un mapeo entre tu Codec y el tipo de objeto Realm Kotlin al que aplica. Continúa al apartado de CodecRegistry en esta página para ver cómo puedes incluir tu Codec.

Para obtener más información sobre las clases e interfaces 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 las clases de Kotlin que especifican. Puedes 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()

  • fromProviders()

  • fromRegistries()

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

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

En el ejemplo anterior, asignamos al CodecRegistry las siguientes implementaciones Codec:

  • IntegerCodec, un Codec que convierte Integers y forma parte del paquete BSON.

  • PowerStatusCodec, nuestra muestra Codec que convierte los valores enum de Kotlin en booleanos BSON.

Puedes recuperar las instancias de Codec de la instancia CodecRegistry del ejemplo anterior utilizando el siguiente código:

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

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

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

Un CodecProvider es una interfaz que contiene métodos abstractos que crean instancias de Codec y las asignan a una instancia de CodecRegistry. De manera similar a la CodecRegistry, la librería BSON utiliza las instancias Codec recuperadas con el método get() para convertir entre los tipos de dato de Kotlin y BSON.

Sin embargo, en casos en los que agregues una clase que contenga campos que requieran los correspondientes objetos Codec, necesitas asegurarte de instanciar los objetos Codec para los campos de la clase antes de instanciar el objeto Codec para la clase. Puedes usar el parámetro CodecRegistry en el método get() para pasar cualquiera de las instancias Codec de las que depende el Codec.

El siguiente ejemplo de código muestra cómo puedes implementar CodecProvider para pasar MonolightCodec cualquier instancia de Codec que necesite en una instancia de CodecRegistry, como el PowerStatusCodec de nuestro ejemplo anterior:

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 ver un ejemplo ejecutable que demuestre operaciones de lectura y operaciones de guardar utilizando estas Codec clases, consulta la sección Ejemplo de códec personalizado de esta guía.

El registro de códecs por defecto es un conjunto de clases CodecProvider que especifican conversiones entre tipos de Kotlin y MongoDB comúnmente utilizados. El driver utiliza automáticamente el registro de códecs por defecto, a menos que se especifique uno diferente.

Si necesitas modificar el comportamiento de una o más clases Codec, pero mantener el comportamiento de la lista de códecs por defecto para las demás clases, puedes especificar todos los registros en orden de prioridad. Por ejemplo, si deseabas anular el comportamiento por defecto de proveedor de un Codec para tipos enumerados con tu MyEnumCodec personalizado, debes añadirlo a la lista de registro antes del registro por defecto de codec, como se muestra en el siguiente ejemplo:

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

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

La clase BsonTypeClassMap contiene una asignación recomendada entre tipos BSON y Kotlin. Puedes utilizar esta clase en tu Codec o CodecProvider personalizada para ayudarte a gestionar a qué tipos de Kotlin decodificar tus BSON types en clases contenedor que implementar 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 de código muestra cómo el tipo de de que corresponde al tipo en la BsonTypeClassMap:

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

Puedes modificar estas asignaciones en tu instancia especificando reemplazos en el constructor BsonTypeClassMap. El siguiente snippet de código muestra cómo puedes reemplazar la asignación para ARRAY en tu instancia de 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)
Java type: java.util.Set

Para obtener una lista completa de los mapeos por defecto, consulte la BsonTypeClassMap Documentación de la API.

Para ver un ejemplo de cómo la clase Document utiliza BsonTypeClassMap, consulte el código fuente del controlador para las siguientes clases:

En esta sección, mostramos cómo puedes implementar Codec y CodecProvider para definir la lógica de codificación y decodificación para una clase personalizada de Kotlin. También le mostramos cómo puede especificar y utilizar sus implementaciones personalizadas para realizar operaciones de insertar y recuperar.

Tip

Serialización de Kotlin

Como alternativa a la implementación de codecs personalizados, puedes usar la serialización de Kotlin para gestionar la codificación y decodificación de tus datos con las clases @Serializable. Puedes elegir la serialización de Kotlin si ya estás familiarizado con el framework o prefieres utilizar un enfoque idiomático de Kotlin. Ver el Serialización en Kotlin documentación para más información.

El siguiente snippet muestra nuestro ejemplo de clase personalizada llamada Monolight y sus campos que queremos almacenar y recuperar de una colección de MongoDB:

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 necesitamos asignar un Codec:

  • powerStatus describe si la luz está encendida o apagada para lo cual utilizamos el PowerStatusCodec que convierte valores enum específicos a booleanos BSON.

  • colorTemperature describe el color de la luz y contiene un valor Int para el cual usamos el IntegerCodec incluido en la librería BSON.

El siguiente ejemplo de código muestra cómo podemos implementar un Codec para la clase Monolight. Ten en cuenta que el constructor espera una instancia de CodecRegistry de la cual recupera las instancias de Codec que necesita para codificar y decodificar sus campos:

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 pongamos las instancias de Codec de los campos a disposición de Monolight, implementamos un CodecProvider personalizado que 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, podemos realizar lo siguiente:

  • Almacena datos de instancias de Monolight en MongoDB

  • Recuperar datos de MongoDB en instancias de Monolight

La siguiente clase de ejemplo contiene código que asigna el MonolightCodecProvider a la instancia de MongoCollection pasándolo al método withCodecRegistry(). La clase de ejemplo también inserta y recupera datos utilizando la clase Monolight y los códecs asociados:

fun main() = runBlocking {
val mongoClient = MongoClient.create("<connection string uri>")
val codecRegistry = CodecRegistries.fromRegistries(
CodecRegistries.fromCodecs(IntegerCodec(), PowerStatusCodec()),
CodecRegistries.fromProviders(MonolightCodecProvider()),
MongoClientSettings.getDefaultCodecRegistry()
)
val database = mongoClient.getDatabase("codecs_example_products")
val collection = database.getCollection<Monolight>("monolights")
.withCodecRegistry(codecRegistry)
// Construct and insert an instance of Monolight
val myMonolight = Monolight(PowerStatus.ON, 5200)
collection.insertOne(myMonolight)
// Retrieve one or more instances of Monolight
val lights = collection.find().toList()
println(lights)
}
[Monolight [powerStatus=ON, colorTemperature=5200]]

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

Volver

Serialización de Kotlin

En esta página