Overview
En esta guía, puede aprender sobre los códecs y las clases de soporte que gestionan la codificación y decodificación de objetos Scala hacia y desde datos BSON en el driver de Scala. La abstracción Codec le permite asignar cualquier tipo de Scala a un tipo BSON correspondiente. Utilice esto para asignar sus objetos de dominio directamente hacia y desde BSON en lugar de depender de un objeto intermedio basado en mapas como Document o BsonDocument.
Interfaz de códec
La interfaz Codec contiene métodos abstractos para codificar y decodificar objetos Scala hacia y desde datos BSON. Implementar estos métodos para definir la lógica de conversión entre BSON y el tipo Scala de su implementación Codec.
Para implementar la interfaz Codec, defina los métodos encode(), decode() y getEncoderClass() de la interfaz. Para ver un ejemplo de código que implementa estos métodos, consulte la sección Ejemplo de códec personalizado básico.
Método encode()
El método encode() codifica una instancia del tipo Scala en BSON para que el driver pueda almacenarla en MongoDB. Este método requiere los siguientes parámetros:
Parameter Type | Descripción |
|---|---|
| Una instancia de una clase que implementa |
| Los datos que codifica el método. El tipo |
| Metadatos sobre el objeto Scala que el método codifica en BSON, incluyendo si se debe almacenar el valor actual en una colección de MongoDB. |
El método encode() no devuelve un valor.
Método decode()
El método decode() utiliza los datos BSON para decodificar una instancia del tipo Scala. Este método requiere los siguientes parámetros:
Parameter Type | Descripción |
|---|---|
| Una instancia de una clase que implementa |
| Metadatos sobre los datos BSON que el método decodifica en un objeto Scala. |
Método getEncoderClass()
El método getEncoderClass() devuelve una instancia del tipo Scala definido en el Codec. Este método proporciona la información de tipo que la máquina virtual de Java (JVM) borra en tiempo de ejecución.
Ejemplo de códec personalizado básico
Las siguientes muestras de código utilizan la característica sellada PowerStatus y la clase PowerStatusCodec para mostrar cómo se puede implementar un Codec personalizado.
La característica sellada PowerStatus utiliza los objetos de caso On y Off para representar los estados de un interruptor eléctrico:
sealed trait PowerStatusobject PowerStatus { case object On extends PowerStatus case object Off extends PowerStatus}La clase PowerStatusCodec implementa la interfaz Codec para codificar valores PowerStatus en los valores booleanos de BSON correspondientes. El método encode() codifica un valor PowerStatus en un valor booleano de BSON y el método decode() decodifica un valor booleano de BSON en un valor PowerStatus.
class PowerStatusCodec extends Codec[PowerStatus] {
override def encode(writer: BsonWriter, value: PowerStatus, encoderContext: EncoderContext): Unit = { if (value != null) { writer.writeBoolean(value == PowerStatus.On) } }
override def decode(reader: BsonReader, decoderContext: DecoderContext): PowerStatus = { if (reader.readBoolean()) PowerStatus.On else PowerStatus.Off }
override def getEncoderClass: Class[PowerStatus] = classOf[PowerStatus]}Para usar la clase PowerStatusCodec, debe agregar una instancia de la clase a su interfaz CodecRegistry, que asigna su Codec al tipo Scala correspondiente. El driver puede codificar y decodificar un tipo solo si el CodecRegistry contiene un Codec para ese tipo. Para aprender a incluir su Codec personalizado en su interfaz CodecRegistry, consulte la sección CodecRegistry de esta página.
Para obtener más información sobre las clases y las interfaces de esta sección, consulta la siguiente documentación de la API:
Colección CodecRegistry
Un CodecRegistry es una colección inmutable de Codec instancias. Para construir una instancia de CodecRegistry, utilice uno de los siguientes métodos de fábrica estática de clase CodecRegistries. Cada método compila el registro a partir de una fuente diferente de Codec instancias:
Método | Descripción |
|---|---|
| Compila un registro a partir de las instancias |
| Compila un registro a partir de las instancias |
| Compila un registro combinando las otras instancias de |
Los siguientes ejemplos utilizan dos implementaciones Codec:
IntegerCodec: UnCodecen el paquete BSON que codifica valoresIntegerde Java en valores enteros de 32bits de BSON.PowerStatusCodec: una muestra
Codecque decodifica los valoresPowerStatusen booleanos BSON.
El siguiente ejemplo muestra cómo construir una instancia de CodecRegistry utilizando el método fromCodecs() para asignar estas implementaciones al registro:
val codecRegistry = CodecRegistries.fromCodecs( new IntegerCodec(), new PowerStatusCodec())El siguiente ejemplo recupera las instancias Codec del ejemplo anterior de la colección CodecRegistry:
val powerStatusCodec: Codec[PowerStatus] = codecRegistry.get(classOf[PowerStatus])val integerCodec: Codec[Integer] = codecRegistry.get(classOf[Integer])Nota
Si llama al método get() en una instancia Codec para una clase no registrada, el driver arroja un CodecConfigurationException.
Registro de códecs por defecto
El registro de códec por defecto es un conjunto de clases CodecProvider que codifican entre tipos de Scala y MongoDB de uso común. El driver utiliza automáticamente el registro de códec por defecto a menos que especifique un registro de códec personalizado. Para obtener más información sobre la interfaz CodecProvider, consulte la sección CodecProvider.
Si debe anular el comportamiento de una o más clases Codec, pero desea mantener el comportamiento del registro de códec por defecto para las otras clases, puede especificar todos los registros en orden de precedencia. Por ejemplo, para anular el comportamiento del proveedor predeterminado de un Codec para un tipo personalizado con su MyEnumCodec, agréguelo a la lista de registros antes del registro de códec predeterminado. El siguiente ejemplo muestra este patrón:
val newRegistry = CodecRegistries.fromRegistries( CodecRegistries.fromCodecs(new MyEnumCodec()), MongoClientSettings.getDefaultCodecRegistry())Para obtener más información sobre las clases y las interfaces de esta sección, consulta la siguiente documentación de la API:
Interfaz CodecProvider
La interfaz CodecProvider contiene métodos abstractos que crean instancias Codec y las asignan a una instancia CodecRegistry. Al igual que la interfaz CodecRegistry, la interfaz CodecProvider define un método get() que devuelve instancias Codec. La librería BSON utiliza estas instancias Codec para codificar entre Scala y los tipos de datos BSON.
Utilice un CodecProvider cuando agregue una clase a su código cuyos campos necesiten los objetos Codec correspondientes. Cuando cada campo requiere su propia instancia Codec, debe crear una instancia de los objetos Codec para cada campo antes de crear una instancia de la instancia Codec para la clase. Utilice el parámetro CodecRegistry en el método get() para pasar cualquiera de las instancias Codec en las que se basa el Codec a un constructor.
El siguiente ejemplo muestra cómo implementar una interfaz CodecProvider. El driver utiliza la interfaz MonolightCodecProvider implementada para crear una instancia MonolightCodec para la clase Monolight.
class MonolightCodecProvider extends CodecProvider {
override def get[T](clazz: Class[T], registry: CodecRegistry): Codec[T] = { if (clazz == classOf[Monolight]) { new MonolightCodec(registry).asInstanceOf[Codec[T]] } else { null } }}Para obtener una implementación completa de una interfaz CodecProvider, incluidas las clases personalizadas, consulta la sección Ejemplo de códec personalizado completo de esta guía.
Para obtener más información sobre las clases y las interfaces de esta sección, consulta la siguiente documentación de la API:
Ejemplo de Codec Personalizado Completo
En esta sección, puedes aprender a implementar las interfaces Codec y CodecProvider para definir la lógica de codificación y decodificación de una clase Scala personalizada. En esta sección también se muestra cómo especificar y utilizar sus implementaciones personalizadas para realizar operaciones de inserción y recuperación.
El siguiente snippet de código muestra la clase personalizada de ejemplo Monolight y sus campos:
case class Monolight( powerStatus: PowerStatus = PowerStatus.Off, colorTemperature: Int = 0)La clase Monolight contiene los siguientes campos, cada uno de los cuales requiere una implementación de interfaz Codec:
powerStatusdescribe si la luz está encendida o apagada. La clase PowerStatusCodec codifica los valoresPowerStatusen booleanos BSON.colorTemperaturedescribe el color de la luz y contiene un valorInt. La claseIntegerCodecincluida en la librería BSON codifica valorescolorTemperatureen enteros BSON de 32bits.
El siguiente ejemplo de código muestra cómo implementar una interfaz Codec para la clase Monolight. El constructor utiliza una instancia de CodecRegistry para recuperar las instancias de Codec que necesita para codificar y decodificar los campos de Monolight.
class MonolightCodec(registry: CodecRegistry) extends Codec[Monolight] {
private val powerStatusCodec: Codec[PowerStatus] = registry.get(classOf[PowerStatus]) private val integerCodec: Codec[Integer] = registry.get(classOf[Integer])
override def encode(writer: BsonWriter, value: Monolight, encoderContext: EncoderContext): Unit = { writer.writeStartDocument() writer.writeName("powerStatus") powerStatusCodec.encode(writer, value.powerStatus, encoderContext) writer.writeName("colorTemperature") integerCodec.encode(writer, value.colorTemperature, encoderContext) writer.writeEndDocument() }
override def decode(reader: BsonReader, decoderContext: DecoderContext): Monolight = { var powerStatus: PowerStatus = PowerStatus.Off var colorTemperature: Int = 0
reader.readStartDocument() while (reader.readBsonType() != BsonType.END_OF_DOCUMENT) { reader.readName() match { case "powerStatus" => powerStatus = powerStatusCodec.decode(reader, decoderContext) case "colorTemperature" => colorTemperature = integerCodec.decode(reader, decoderContext) case "_id" => reader.readObjectId() case _ => reader.skipValue() } } reader.readEndDocument()
Monolight(powerStatus, colorTemperature) }
override def getEncoderClass: Class[Monolight] = classOf[Monolight]}El siguiente ejemplo de código muestra cómo construir instancias Codec para los campos de la clase Monolight e implementar un CodecProvider personalizado:
class MonolightCodecProvider extends CodecProvider {
override def get[T](clazz: Class[T], registry: CodecRegistry): Codec[T] = { if (clazz == classOf[Monolight]) { new MonolightCodec(registry).asInstanceOf[Codec[T]] } else { null } }}El método get() devuelve un nuevo MonolightCodec cuando el driver solicita un Codec para la clase Monolight. El método pasa el CodecRegistry al constructor MonolightCodec para que el MonolightCodec pueda recuperar las instancias Codec de sus campos, como PowerStatusCodec y IntegerCodec. Si el driver solicita un Codec para cualquier otra clase, el método devuelve null.
Después de definir la lógica de conversión, puede realizar las siguientes operaciones:
Almacene datos de instancias de la clase
Monolighten MongoDBRecuperar datos de MongoDB en instancias de la clase
Monolight
El siguiente ejemplo asigna la clase MonolightCodecProvider a la instancia MongoCollection pasándola al método withCodecRegistry(). A continuación, el ejemplo inserta una nueva instancia Monolight en la colección llamando al método insertOne() y, a continuación, llamando al método find() para devolver la instancia Monolight almacenada. El resultado muestra la instancia Monolight recuperada, lo que confirma que los códecs personalizados codificaron y descodificaron correctamente los datos.
object MonolightCodecExample {
def main(args: Array[String]): Unit = { val uri = "<connection string URI>" val mongoClient = MongoClient(uri)
val codecRegistry = CodecRegistries.fromRegistries( CodecRegistries.fromCodecs( new IntegerCodec(), new PowerStatusCodec() ), CodecRegistries.fromProviders(new MonolightCodecProvider()), MongoClientSettings.getDefaultCodecRegistry() )
val database = mongoClient.getDatabase("codecs_example_products") val collection: MongoCollection[Monolight] = database .getCollection[Monolight]("monolights") .withCodecRegistry(codecRegistry)
val myMonolight = Monolight(PowerStatus.On, 5200) Await.result( collection.insertOne(myMonolight).toFuture(), Duration.Inf )
val lights = Await.result( collection.find().toFuture(), Duration.Inf ) println(lights)
mongoClient.close() }}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: