Overview
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 Java hacia y desde datos BSON en el controlador Java de MongoDB. Codec La abstracción permite mapear cualquier tipo de Java a su tipo BSON correspondiente. Esto permite mapear los objetos de dominio directamente desde y hacia BSON, en lugar de usar un objeto intermedio basado en mapeo, como Document o BsonDocument.
Puede aprender a especificar la lógica de codificación y decodificación personalizada utilizando la abstracción Codec y ver implementaciones de ejemplo en las siguientes secciones:
Si está personalizando la lógica de codificación y decodificación para objetos Java simples (POJO), lea nuestra guía sobre Personalización de POJO.
Códec
La interfaz Codec contiene métodos abstractos para serializar y deserializar objetos Java a datos BSON. Puede definir la lógica de conversión entre BSON y su objeto Java en la implementación de esta interfaz.
Para implementar la interfaz Codec, anule los métodos abstractos encode(), decode() y getEncoderClass().
El método encode() requiere los siguientes parámetros:
Parameter Type | Descripción |
|---|---|
| Una instancia de una clase que implementa |
| Los datos que codifica su implementación. El tipo debe coincidir con la variable de tipo asignada a su implementación. |
| Contiene metainformación sobre los datos del objeto Java que codifica en BSON, incluido si se debe almacenar el valor actual en una colección MongoDB. |
Este método utiliza la instancia BsonWriter para enviar el valor codificado a MongoDB y no devuelve un valor.
El método decode() devuelve la instancia del objeto Java rellena con el valor de los datos BSON. Este método requiere los siguientes parámetros:
Parameter Type | Descripción |
|---|---|
| Una instancia de una clase que implementa |
| Contiene información sobre los datos BSON que decodifica en un objeto Java. |
El método getEncoderClass() devuelve una instancia de clase de la clase Java ya que Java no puede inferir el tipo debido al borrado de tipo.
Vea los siguientes ejemplos de código que muestran cómo puede implementar un Codec personalizado.
La enumeración PowerStatus contiene los valores "ON" y "OFF" para representar los estados de un interruptor eléctrico.
public enum PowerStatus { ON, OFF }
La clase PowerStatusCodec implementa Codec para convertir los valores Java enum en los correspondientes valores booleanos BSON. El método encode() convierte un PowerStatus a un booleano BSON y el método decode() realiza la conversión en la dirección opuesta.
public class PowerStatusCodec implements Codec<PowerStatus> { public void encode(BsonWriter writer, PowerStatus value, EncoderContext encoderContext) { if (value != null) { writer.writeBoolean(value.equals(PowerStatus.ON) ? Boolean.TRUE : Boolean.FALSE); } } public PowerStatus decode(BsonReader reader, DecoderContext decoderContext) { return reader.readBoolean() ? PowerStatus.ON : PowerStatus.OFF; } public Class<PowerStatus> getEncoderClass() { return PowerStatus.class; } }
Puede agregar una instancia de PowerStatusCodec a su,CodecRegistry la cual contiene una asignación entre su Codec y el tipo de objeto Java al que se aplica. Continúe en la sección CodecRegistry de esta página para ver cómo puede incluir Codec su.
Para obtener más información sobre las clases e interfaces en esta sección, consulte la siguiente documentación de API:
Registro de códecs
Un CodecRegistry es una colección inmutable de instancias de Codec que codifican y decodifican las clases Java que especifican. Puedes utilizar cualquiera de los siguientes métodos de fábrica estáticos de la clase CodecRegistries para construir un CodecRegistry a partir de las instancias de Codec contenidas en los tipos asociados:
fromCodecs()fromProviders()fromRegistries()
El siguiente fragmento de código muestra cómo construir un CodecRegistry utilizando el método fromCodecs():
CodecRegistry codecRegistry = CodecRegistries.fromCodecs(new IntegerCodec(), new PowerStatusCodec());
En el ejemplo anterior, asignamos a CodecRegistry las siguientes implementaciones de Codec:
IntegerCodec, unCodecque convierteIntegersy es parte del paquete BSON.PowerStatusCodec, nuestra muestra
Codecque convierte ciertas cadenas de Java en valores booleanos BSON.
Puedes recuperar las instancias de Codec de la instancia CodecRegistry del ejemplo anterior utilizando el siguiente código:
Codec<PowerStatus> powerStatusCodec = codecRegistry.get(PowerStatus.class); Codec<Integer> integerCodec = codecRegistry.get(Integer.class);
Si intenta recuperar una instancia Codec para una clase que no está registrada, el método get() genera 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:
Proveedor de códecs
Una interfaz CodecProvider contiene métodos abstractos que crean instancias Codec y las asignan a una instancia CodecRegistry. Al igual que la CodecRegistry, la biblioteca BSON utiliza las instancias Codec recuperadas por el método get() para convertir entre tipos de datos Java y BSON.
Sin embargo, si agrega una clase que contiene campos que requieren los objetos Codec correspondientes, debe asegurarse de instanciar los objetos Codec para los campos de la clase antes de instanciar los objetos Codec para la clase. Puede usar el parámetro CodecRegistry en el método get() para pasar cualquiera de las instancias Codec de las que depende el objeto Codec.
El siguiente ejemplo de código muestra cómo se puede implementar CodecProvider para pasar a MonolightCodec cualquier instancia de Codec que necesite en una instancia de CodecRegistry como la PowerStatusCodec de nuestro ejemplo anterior:
public class MonolightCodecProvider implements CodecProvider { public MonolightCodecProvider() {} public <T> Codec<T> get(Class<T> clazz, CodecRegistry registry) { if (clazz == Monolight.class) { return (Codec<T>) new MonolightCodec(registry); } // return null when not a provider for the requested class return null; } }
Para ver un ejemplo ejecutable que demuestre operaciones de lectura y escritura utilizando estas Codec clases, consulte la sección Ejemplo de códec personalizado de esta guía.
Al trabajar con POJOs, considere usar PojoCodecProvider para minimizar el código duplicado, convertir los tipos de datos más comunes y personalizar su comportamiento.Consulte nuestra guía de personalización de POJOs para obtener más información.
Registro de códecs predeterminado
El registro de codecs por defecto es un conjunto de CodecProvider clases que especifican la conversión entre los tipos de Java y MongoDB comúnmente usados. El driver utiliza automáticamente el registro de codecs por defecto a menos que especifiques uno diferente.
Si necesita anular el comportamiento de una o más clases Codec, pero conservar el comportamiento del registro de códecs predeterminado para las demás clases, puede especificar todos los registros en orden de precedencia. Por ejemplo, supongamos que desea anular el comportamiento predeterminado del proveedor de un Codec para tipos de enumeración con su MyEnumCodec personalizado; debe añadirlo a la lista de registros antes que al registro de códecs predeterminado, como se muestra en el siguiente ejemplo:
CodecRegistry newRegistry = CodecRegistries.fromRegistries( CodecRegistries.fromCodecs(new MyEnumCodec()), MongoClientSettings.getDefaultCodecRegistry());
Para obtener más información sobre las clases e interfaces de esta sección, consulte las siguientes secciones de la documentación de API:
Mapa de clases de tipos Bson
La clase BsonTypeClassMap contiene una asignación recomendada entre tipos BSON y Java. Puede usar esta clase en sus Codec o CodecProvider personalizados para gestionar qué tipos Java decodificar sus tipos BSON en 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 fragmento de código muestra cómo recuperar el tipo de clase Java que corresponde al tipo BSON en la instancia predeterminada BsonTypeClassMap:
BsonTypeClassMap bsonTypeClassMap = new BsonTypeClassMap(); Class<?> clazz = bsonTypeClassMap.get(BsonType.ARRAY); System.out.println("Java type: " + clazz.getName());
Este código genera lo siguiente:
Java type: java.util.List
Puede modificar estas asignaciones en su instancia especificando reemplazos en el constructor BsonTypeClassMap. El siguiente fragmento de código muestra cómo reemplazar la asignación de ARRAY en su instancia BsonTypeClassMap con la clase Set:
Map<BsonType, Class<?>> replacements = new HashMap<BsonType, Class<?>>(); replacements.put(BsonType.ARRAY, Set.class); BsonTypeClassMap bsonTypeClassMap = new BsonTypeClassMap(replacements); Class<?> clazz = bsonTypeClassMap.get(BsonType.ARRAY); System.out.println("Java type: " + clazz.getName());
Este código genera lo siguiente:
Java type: java.util.Set
Para obtener una lista completa de las asignaciones predeterminadas, consulte la documentación de la API BsonTypeClassMap.
Para ver un ejemplo de cómo la clase Document usa BsonTypeClassMap, consulte el código fuente del controlador para las siguientes clases:
Ejemplo de códec personalizado
En esta sección, mostramos cómo implementar Codec y CodecProvider para definir la lógica de codificación y decodificación de una clase Java personalizada. También mostramos cómo especificar y usar sus implementaciones personalizadas para realizar operaciones de inserción y recuperación.
El siguiente fragmento de código muestra nuestra clase personalizada de ejemplo llamada Monolight y sus campos que queremos almacenar y recuperar de una colección MongoDB:
public class Monolight { private PowerStatus powerStatus = PowerStatus.OFF; private Integer colorTemperature; public Monolight() {} // ...
Esta clase contiene los siguientes campos, a cada uno de los cuales debemos asignarle un Codec:
powerStatusdescribe si la luz está "encendida" o "apagada", para lo cual utilizamos PowerStatusCodec que convierte valores de enumeración específicos en booleanos BSON.colorTemperaturedescribe el color de la luz y contiene un valorIntegerpara el cual usamos elIntegerCodecincluido en la biblioteca BSON.
El siguiente ejemplo de código muestra cómo implementar un Codec para la clase Monolight. Tenga en cuenta que el constructor espera una instancia de CodecRegistry, de la cual recupera las instancias Codec necesarias para codificar y decodificar sus campos:
public class MonolightCodec implements Codec<Monolight>{ private Codec<PowerStatus> powerStatusCodec; private Codec<Integer> integerCodec; public MonolightCodec(CodecRegistry registry) { this.powerStatusCodec = registry.get(PowerStatus.class); this.integerCodec = registry.get(Integer.class); } // Defines an encode() method to convert Monolight enum values to BSON values public void encode(BsonWriter writer, Monolight value, EncoderContext encoderContext) { writer.writeStartDocument(); writer.writeName("powerStatus"); powerStatusCodec.encode(writer, value.getPowerStatus(), encoderContext); writer.writeName("colorTemperature"); integerCodec.encode(writer, value.getColorTemperature(), encoderContext); writer.writeEndDocument(); } // Defines a decode() method to convert BSON values to Monolight enum values public Monolight decode(BsonReader reader, DecoderContext decoderContext) { Monolight monolight = new Monolight(); reader.readStartDocument(); while (reader.readBsonType() != BsonType.END_OF_DOCUMENT) { String fieldName = reader.readName(); if (fieldName.equals("powerStatus")) { monolight.setPowerStatus(powerStatusCodec.decode(reader, decoderContext)); } else if (fieldName.equals("colorTemperature")) { monolight.setColorTemperature(integerCodec.decode(reader, decoderContext)); } else if (fieldName.equals("_id")){ reader.readObjectId(); } } reader.readEndDocument(); return monolight; } // Returns an instance of the Monolight class, since Java cannot infer the class type public Class<Monolight> getEncoderClass() { return Monolight.class; } }
Para garantizar que las instancias Codec de los campos estén disponibles para Monolight, implementamos un CodecProvider personalizado que se muestra en el siguiente ejemplo de código:
public class MonolightCodecProvider implements CodecProvider { public MonolightCodecProvider() {} public <T> Codec<T> get(Class<T> clazz, CodecRegistry registry) { if (clazz == Monolight.class) { return (Codec<T>) new MonolightCodec(registry); } // return null when not a provider for the requested class return null; } }
Después de definir la lógica de conversión, podemos realizar lo siguiente:
Almacena datos de instancias de
Monolighten MongoDBRecuperar datos de MongoDB en instancias de
Monolight
La siguiente clase de ejemplo contiene código que asigna el valor MonolightCodecProvider a la instancia MongoCollection pasándolo al método withCodecRegistry(). La clase de ejemplo también inserta y recupera datos mediante la clase Monolight y los códecs asociados:
public class MonolightCodecExample { public static void main(String[] args) { String uri = "<MongoDB connection URI>"; try (MongoClient mongoClient = MongoClients.create(uri)) { CodecRegistry codecRegistry = CodecRegistries.fromRegistries( CodecRegistries.fromCodecs(new IntegerCodec(), new PowerStatusCodec()), CodecRegistries.fromProviders(new MonolightCodecProvider()), MongoClientSettings.getDefaultCodecRegistry()); MongoDatabase database = mongoClient.getDatabase("codecs_example_products"); MongoCollection<Monolight> collection = database.getCollection("monolights", Monolight.class).withCodecRegistry(codecRegistry); // construct and insert an instance of Monolight Monolight myMonolight = new Monolight(); myMonolight.setPowerStatus(PowerStatus.ON); myMonolight.setColorTemperature(5200); collection.insertOne(myMonolight); // retrieve one or more instances of Monolight List<Monolight> lights = new ArrayList<>(); collection.find().into(lights); System.out.println(lights); } } }
Si ejecuta el ejemplo anterior, debería ver el siguiente resultado:
[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 API: