Overview
En esta guía, puedes aprender acerca de Códecs y las clases de apoyo que gestionan la codificación y decodificación de objetos Java hacia y desde datos BSON en el driver Java Reactive Streams. La abstracción Codec te permite mapear cualquier tipo de Java a un tipo BSON correspondiente. Puedes usar esta abstracción para mapear tus objetos de dominio directamente hacia y desde BSON en lugar de usar un objeto intermedio basado en mapas como Document o BsonDocument.
Aprenda cómo especificar la lógica personalizada de codificación y decodificación utilizando la abstracción Codec en las siguientes secciones:
Para obtener información sobre cómo personalizar la lógica de codificación y decodificación para objetos Java simples y antiguos (POJO), consulte la guía Utilizar POJO para modelar los datos.
Implementar códecs
La interfaz Codec contiene métodos abstractos para serializar y deserializar objetos Java en datos BSON. Defina su lógica de conversión entre BSON y su objeto Java en su 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 |
|---|---|
| Una instancia de una clase que implementa |
| Los datos que tu implementación codifica. El tipo debe coincidir con la variable de tipo asignada a tu implementación. |
| Contiene metadatos sobre los datos del objeto Java que codifica en BSON, incluyendo si almacenar el valor actual en una colección de MongoDB. |
Este método utiliza la instancia BsonWriter para enviar el valor codificado a MongoDB y no devuelve ningún valor.
El método decode() devuelve su instancia de objeto Java poblada 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 a la eliminación de tipos.
Ejemplo
Los siguientes ejemplos de código muestran cómo implementar un Codec personalizado.
El enum ReadStatus contiene los valores READ y UNREAD para representar si se ha leído un libro.
public enum ReadStatus { READ, UNREAD }
La clase ReadStatusCodec implementa Codec para convertir los valores Java enum en los correspondientes valores booleanos BSON. El método encode() convierte un ReadStatus a un booleano BSON y el método decode() realiza la conversión en la dirección opuesta.
public class ReadStatusCodec implements Codec<ReadStatus> { public void encode(BsonWriter writer, ReadStatus value, EncoderContext encoderContext) { writer.writeBoolean(value.equals(ReadStatus.READ) ? Boolean.TRUE : Boolean.FALSE); } public ReadStatus decode(BsonReader reader, DecoderContext decoderContext) { return reader.readBoolean() ? ReadStatus.READ : ReadStatus.UNREAD; } public Class<ReadStatus> getEncoderClass() { return ReadStatus.class; } }
Puede agregar una instancia del ReadStatusCodec a su CodecRegistry, que contiene una asignación entre su Codec y el tipo de objeto Realm Java al que se aplica. Continúe con la sección CodecRegistry de esta página para ver cómo incluir su Codec.
Para obtener más información sobre las clases y las interfaces de esta sección, consulta la siguiente documentación de la API:
Utilice un CodecRegistry
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 snippet de código muestra cómo construir un CodecRegistry usando el método fromCodecs():
CodecRegistry codecRegistry = CodecRegistries.fromCodecs(new IntegerCodec(), new ReadStatusCodec());
En el ejemplo anterior, CodecRegistry incluye las siguientes implementaciones Codec:
IntegerCodec, unCodecque convierteIntegersy forma parte del paquete BSON.ReadStatusCodec, nuestra muestra
Codecque convierte los valores enum de Java en booleanos BSON.
Puede recuperar las instancias de Codec de la instancia CodecRegistry del ejemplo anterior utilizando el siguiente código:
Codec<ReadStatus> readStatusCodec = codecRegistry.get(ReadStatus.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() lanza un 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:
Utilice un CodecProvider
Un CodecProvider es una interfaz que contiene métodos abstractos que crean instancias de Codec y las asignan a una instancia CodecRegistry. De manera similar al CodecRegistry, la biblioteca BSON utiliza las instancias de Codec recuperadas por el método get() para convertir entre los tipos de datos Java y BSON.
Sin embargo, si agrega una clase que contiene campos que requieren objetos Codec correspondientes, debe instanciar los objetos Codec para los campos de la clase antes de instanciar el Codec para la clase. Puede usar el parámetro CodecRegistry en el método get() para pasar cualquier instancia Codec de la que dependa el Codec.
El siguiente ejemplo de código muestra cómo implementar CodecProvider para pasar BookCodec cualquier instancia de Codec que necesite en una instancia de CodecRegistry, como el ReadStatusCodec del ejemplo anterior:
public class BookCodecProvider implements CodecProvider { public BookCodecProvider() {} public <T> Codec<T> get(Class<T> clazz, CodecRegistry registry) { if (clazz == Book.class) { return (Codec<T>) new BookCodec(registry); } // return null when not a provider for the requested class return null; } }
Para ver un ejemplo ejecutable que demuestre operaciones de lectura y guardar utilizando estas Codec clases, consulta la sección Ejemplo de códec personalizado de esta guía.
Cuando trabaje con POJO, considere usar el PojoCodecProvider para minimizar la duplicación de código al convertir tipos de datos de uso común y personalizar su comportamiento. Consulte nuestra guía Usar POJO para modelar sus datos para obtener más información.
Usar el registro de códecs por defecto
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 debe anular el comportamiento de una o más clases Codec, pero mantener el comportamiento del registro de códecs por defecto para las demás clases, puede especificar todos los registros en orden de precedencia. Por ejemplo, para anular el comportamiento del proveedor por defecto de un Codec para tipos enumerados con su MyEnumCodec personalizado, agréguelo a la lista de registros antes del registro de códecs por defecto, como se muestra en el siguiente ejemplo:
CodecRegistry newRegistry = CodecRegistries.fromRegistries( CodecRegistries.fromCodecs(new MyEnumCodec()), MongoClientSettings.getDefaultCodecRegistry());
Para más información sobre las clases e interfaces de esta sección, consulte las siguientes secciones de la documentación de la API:
Personalizar asignaciones de tipos
La clase BsonTypeClassMap contiene una asignación recomendada entre BSON y Java types. Puede utilizar esta clase en su Codec o CodecProvider personalizada para ayudarle a gestionar a qué clases contenedoras que implementan Iterable o Map, tales como la clase Document, decodificar sus tipos BSON.
Puedes agregar o modificar la asignación BsonTypeClassMap por defecto pasando un Map que contenga nuevas entradas o reemplazos.
El siguiente código snippet muestra cómo recuperar el tipo de clase Java que corresponde al tipo BSON en la instancia por defecto de 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
Puedes modificar estas asignaciones en tu instancia especificando reemplazos en el constructor BsonTypeClassMap. El siguiente ejemplo muestra cómo reemplazar la asignación de ARRAY en tu 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 por defecto, consulte la Documentación de la API de BsonTypeClassMap.
Tip
Para ver ejemplos de cómo la clase Document utiliza BsonTypeClassMap, consulte el código fuente del driver para las clases DocumentCodecProvider y DocumentCodec.
Ejemplo de Codec Personalizado
Esta sección muestra cómo implementar Codec y CodecProvider para definir la lógica de codificación y decodificación para una clase Java personalizada. También muestra cómo especificar y utilizar sus implementaciones personalizadas para realizar operaciones de inserción y recuperación.
El siguiente ejemplo define una clase personalizada llamada Book y sus campos para el almacenamiento y la recuperación en una colección de MongoDB:
public class Book { private String title; private ReadStatus readStatus = ReadStatus.UNREAD; private Integer pageCount; public Book() {} // ...
Esta clase contiene los siguientes campos, cada uno de los cuales requiere un Codec:
titlecontiene un valorStringpara el cual el ejemplo utiliza elStringCodecincluido en la librería BSON.readStatusdescribe si se ha leído un libro, para lo cual el ejemplo utiliza el ReadStatusCodec personalizado que convierte los valores de enumeración en booleanos BSON.pageCountcontiene un valorIntegerpara el cual el ejemplo utiliza elIntegerCodecincluido en la librería BSON.
El siguiente ejemplo de código muestra cómo implementar un Codec para la clase Book. Ten en cuenta que el constructor espera una instancia de CodecRegistry de la cual recupera las instancias de Codec que necesita para codificar y decodificar sus campos:
public class BookCodec implements Codec<Book> { private Codec<String> stringCodec; private Codec<ReadStatus> readStatusCodec; private Codec<Integer> integerCodec; public BookCodec(CodecRegistry registry) { this.stringCodec = registry.get(String.class); this.readStatusCodec = registry.get(ReadStatus.class); this.integerCodec = registry.get(Integer.class); } // Defines an encode() method to convert Book field values to BSON values public void encode(BsonWriter writer, Book value, EncoderContext encoderContext) { writer.writeStartDocument(); writer.writeName("title"); stringCodec.encode(writer, value.getTitle(), encoderContext); writer.writeName("readStatus"); readStatusCodec.encode(writer, value.getReadStatus(), encoderContext); writer.writeName("pageCount"); integerCodec.encode(writer, value.getPageCount(), encoderContext); writer.writeEndDocument(); } // Defines a decode() method to convert BSON values to Book field values public Book decode(BsonReader reader, DecoderContext decoderContext) { Book book = new Book(); reader.readStartDocument(); while (reader.readBsonType() != BsonType.END_OF_DOCUMENT) { String fieldName = reader.readName(); if (fieldName.equals("title")) { book.setTitle(stringCodec.decode(reader, decoderContext)); } else if (fieldName.equals("readStatus")) { book.setReadStatus(readStatusCodec.decode(reader, decoderContext)); } else if (fieldName.equals("pageCount")) { book.setPageCount(integerCodec.decode(reader, decoderContext)); } else if (fieldName.equals("_id")) { reader.readObjectId(); } else { reader.skipValue(); } } reader.readEndDocument(); return book; } // Returns an instance of the Book class, since Java cannot infer the class type public Class<Book> getEncoderClass() { return Book.class; } }
Para que las instancias de Codec de los campos estén disponibles para Book, implemente un CodecProvider personalizado que se muestra en el siguiente ejemplo de código:
public class BookCodecProvider implements CodecProvider { public BookCodecProvider() {} public <T> Codec<T> get(Class<T> clazz, CodecRegistry registry) { if (clazz == Book.class) { return (Codec<T>) new BookCodec(registry); } // return null when not a provider for the requested class return null; } }
Después de definir la lógica de conversión, puede realizar las siguientes operaciones:
Almacena datos de instancias de
Booken MongoDBRecuperar datos de MongoDB en instancias de
Book
La siguiente clase de ejemplo contiene código que asigna el BookCodecProvider a la instancia de MongoCollection pasándolo al método withCodecRegistry(). La clase de ejemplo también inserta y recupera datos utilizando la clase Book y los códecs asociados:
public class BookCodecExample { public static void main(String[] args) { String uri = "<MongoDB connection URI>"; try (MongoClient mongoClient = MongoClients.create(uri)) { CodecRegistry codecRegistry = CodecRegistries.fromRegistries( CodecRegistries.fromCodecs(new ReadStatusCodec()), CodecRegistries.fromProviders(new BookCodecProvider()), MongoClientSettings.getDefaultCodecRegistry()); MongoDatabase database = mongoClient.getDatabase("codecs_example_db"); MongoCollection<Book> collection = database.getCollection("books", Book.class) .withCodecRegistry(codecRegistry); // construct and insert an instance of Book Book myBook = new Book(); myBook.setTitle("The Hobbit"); myBook.setReadStatus(ReadStatus.READ); myBook.setPageCount(310); Mono.from(collection.insertOne(myBook)).block(); // retrieve one or more instances of Book Flux.from(collection.find()) .doOnNext(System.out::println) .blockLast(); } } }
Si ejecuta el ejemplo anterior, el resultado será similar al siguiente:
Book [title=The Hobbit, readStatus=READ, pageCount=310]
Documentación de la API
Para obtener más información sobre los métodos y clases mencionados en esta guía, consulta la siguiente Documentación de la API: