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 Codecs y clases de soporte que se encargan de la codificación y decodificación de objetos Kotlin hacia y desde datos BSON. El 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 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 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 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.

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

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 de enum de Kotlin a 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 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 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{: queCodec Integersconvierte. Forma parte del paquete BSON.

  • PowerStatusCodec: muestra Codec de la sección anterior que convierte valores enumerados 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 de 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 y las interfaces de esta sección, consulta la siguiente documentación de la 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 usando las clases Codec, consulta la sección Ejemplo de Codec 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 y las interfaces de esta sección, consulta la siguiente documentación de la 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 demuestra cómo se puede implementar las interfaces Codec y CodecProvider para definir la lógica de codificación y decodificación para una clase personalizada de Kotlin. Muestra cómo puedes especificar y utilizar tus 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 de Int. Para este campo, usa IntegerCodec proporcionado en la librería 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 de 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 asegurar que las instancias de Codec para los campos estén disponibles para la clase Monolight, implementa 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, consulta la siguiente documentación de la API:

Volver

Serialización

En esta página