Join us at MongoDB.local London on 7 May to unlock new possibilities for your data. Use WEB50 to save 50%.
Register now >
Docs Menu
Docs Home
/ /

Codifica 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 te permite mapear cualquier tipo de Kotlin a un tipo BSON correspondiente. Puedes utilizar esta funcionalidad para mapear objetos de dominio directamente hacia y desde BSON, en lugar de utilizar clases 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

Tip

Serialización de Kotlin

Puedes usar la serialización de Kotlin para gestionar tu codificación y decodificación de datos con las clases @Serializable en lugar de implementar codecs personalizados. Podrías elegir la serialización de Kotlin si ya estás familiarizado con la librería kotlinx.serialization o prefieres utilizar un enfoque idiomático de Kotlin. Para obtener más información, consulte la Guía de 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, 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 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 tu implementación codifica. El tipo debe coincidir con la variable de tipo asignada a tu implementación.

encoderContext

Contiene metainformación acerca de los datos del objeto Kotlin que codifica en BSON, incluyendo si se debe almacenar el valor actual en una colección de 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 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

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.

El enum 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 tu CodecRegistry. Consulta la sección CodecRegistry de esta página para aprender 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 la API:

Un CodecRegistry es una colección inmutable de instancias Codec que codifican y decodifican clases de Kotlin. Se puede utilizar cualquier método de fábrica estático de la clase CodecRegistries para construir un CodecRegistry a partir de las instancias Codec contenidas en los tipos asociados:

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

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

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

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 de Codec de la instancia de 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:

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

Sin embargo, en los casos en los que agregues una clase que contenga campos que requieran objetos Codec correspondientes, asegúrate de instanciar los objetos Codec para los campos de la clase antes de instanciar el Codec para toda la clase. Puedes usar el parámetro CodecRegistry en el método CodecProvider.get() para pasar cualquiera de las instancias Codec de las que depende el 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 por defecto es un conjunto de clases de CodecProvider que especifican la conversión entre objetos de uso común de Kotlin y tipos de MongoDB. El driver utiliza automáticamente el registro de codec por defecto a menos que se especifique uno diferente.

Para sobrescribir el comportamiento de una o más clases Codec, pero mantener el comportamiento del registro de códec por defecto para las otras clases, puedes especificar los registros en orden de precedencia. Por ejemplo, suponga que desea reemplazar el comportamiento del proveedor predeterminado de un Codec para tipos de enumeración con su propio MyEnumCodec personalizado. Debes añadirlo a la lista de registro en una posición antes del registro de códecs por defecto, 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 BSON y los tipos de Kotlin. Puede utilizar esta clase en su Codec o CodecProvider personalizado para ayudarle a gestionar a qué tipos de Kotlin desea decodificar sus BSON types. 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 de Kotlin que corresponde al tipo de arreglo BSON en la instancia predeterminada de BsonTypeClassMap:

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

Puedes modificar estas asignaciones en tu instancia especificando reemplazos en el constructor BsonTypeClassMap. El siguiente snippet muestra cómo se puede reemplazar la asignación para el tipo de arreglo BSON en su 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)
Kotlin class name: java.util.Set

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

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 que requieren cada uno un Codec correspondiente para gestionar 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:

  • Almacene instancias de Monolight en MongoDB

  • Recuperar documentos de MongoDB como instancias de Monolight

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