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.
To implement the Codec interface, define the interface's encode(), decode(), and getEncoderClass() methods. To see a code example that implements these methods, see the Basic Custom Codec Example section.
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 PowerStatus object 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] }
To use the PowerStatusCodec class, you must add an instance of the class to your CodecRegistry interface, which maps your Codec to the corresponding Scala type. The driver can encode and decode a type only if the CodecRegistry contains a Codec for that type. To learn how to include your custom Codec in your CodecRegistry interface, see the CodecRegistry section of this page.
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: A sample
Codecthat decodesPowerStatusvalues to BSON Booleans.
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
The default codec registry is a set of CodecProvider classes that encode between commonly used Scala and MongoDB types. The driver automatically uses the default codec registry unless you specify a custom codec registry. To learn more about the CodecProvider interface, see the CodecProvider section.
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 } } }
For a complete implementation of a CodecProvider interface, including custom classes, see the Complete Custom Codec Example section of this guide.
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:
powerStatusdescribes if the light is switched on or off. The PowerStatusCodec class encodesPowerStatusvalues to BSON Booleans.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: