Codecs
Nesta página
Visão geral
Neste guia, você pode aprender sobre Codecs e as classes de suporte que lidam com a codificação e decodificação de objetos Java de e para dados BSON no driver Java do MongoDB. A abstração Codec
permite mapear qualquer tipo Java para um tipo BSON correspondente. Você pode usar isso para mapear seus objetos de domínio diretamente de e para o BSON, em vez de usar um objeto intermediário baseado em mapa, como Document
ou BsonDocument
.
Você pode aprender a especificar a lógica de codificação e decodificação personalizada usando a Codec
abstração e exibir implementações de exemplo nas seções a seguir:
Se estiver personalizando a lógica de codificação e decodificação para objetos Java antigos e simples (POJOs), leia nosso guia sobre personalização de POJO.
Codec
A interface do Codec
contém métodos abstratos para serializar e desserializar objetos Java para dados BSON. Você pode definir sua lógica de conversão entre BSON e seu objeto Java em sua implementação desta interface.
Para implementar a interface do Codec
, substitua os métodos abstratos encode()
, decode()
e getEncoderClass()
.
O método encode()
exige os seguintes parâmetros:
Parameter Type | Descrição |
---|---|
| Uma instância de uma classe que implementa o |
| Os dados que sua implementação codifica. O tipo deve corresponder à variável de tipo atribuída à sua implementação. |
| Contém informação meta sobre os dados de objeto Java que codifica para BSON, incluindo se deve armazenar o valor atual em uma coleção MongoDB. |
Este método utiliza a instância BsonWriter
para enviar o valor codificado para o MongoDB e não retorna um valor.
O método decode()
retorna sua instância de objeto Java preenchida com o valor dos dados BSON. Este método requer os seguintes parâmetros:
Parameter Type | Descrição |
---|---|
| Uma instância de uma classe que implementa o |
| Contém informações sobre os dados BSON que decodifica para um objeto Java. |
O método getEncoderClass()
retorna uma instância de classe Java uma vez que Java não pode inferir o tipo devido ao apagamento do tipo.
Consulte os seguintes exemplos de código que mostram como você pode implementar um Codec
personalizado.
O PowerStatus
enum contém os valores "ON" e "OFF" para representar os estados de um interruptor elétrico.
public enum PowerStatus { ON, OFF }
A classe PowerStatusCodec
implementa Codec
para converter os valores Java enum
em valores booleanos BSON correspondentes. O método encode()
converte um PowerStatus
em um booleano BSON e o método decode()
executa a conversão na direção oposta.
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; } }
É possível adicionar uma instância do PowerStatusCodec
que contém um mapeamento entre seu Codec
e o tipo de objeto Java ao qual ele se aplica ao seu CodecRegistry
. Consulte a seção CodecRegistry adiante nesta página para ver como incluir seu Codec
.
Para obter mais informações sobre as classes e interfaces nesta seção, consulte a seguinte Documentação da API:
CodecRegistry
CodecRegistry
Um coleção é uma coleção imutável de Codec
instâncias que codificam e decodificam as classes Java especificadas. Você pode usar qualquer um dos seguintes métodos de fábrica estática de classe CodecRegistries
para construir uma CodecRegistry
a partir das instâncias Codec
contidas nos tipos associados:
fromCodecs()
fromProviders()
fromRegistries()
O seguinte trecho de código mostra como construir um CodecRegistry
utilizando o método fromCodecs()
:
CodecRegistry codecRegistry = CodecRegistries.fromCodecs(new IntegerCodec(), new PowerStatusCodec());
No exemplo anterior, atribuímos a CodecRegistry
as seguintes implementações de Codec
:
IntegerCodec
, umCodec
que converteIntegers
e faz parte do pacote BSON.PowerStatusCodec, nossa amostra
Codec
que converte determinadas strings Java em booleanos BSON.
Você pode recuperar as instâncias Codec
da instância CodecRegistry
do exemplo anterior usando o código a seguir:
Codec<PowerStatus> powerStatusCodec = codecRegistry.get(PowerStatus.class); Codec<Integer> integerCodec = codecRegistry.get(Integer.class);
Se você tentar recuperar uma instância do Codec
para uma classe que não está registrada, o método do get()
lançará uma exceção do CodecConfigurationException
.
Para obter mais informações sobre as classes e interfaces nesta seção, consulte a seguinte Documentação da API:
CodecProvider
Um CodecProvider
é uma interface que contém métodos abstratos que criam instâncias do Codec
e atribuem a uma instância do CodecRegistry
. Semelhante ao CodecRegistry
, a biblioteca BSON utiliza as instâncias do Codec
recuperadas pelo método get()
para converter entre Java e tipos de dados BSON.
No entanto, nos casos em que você adiciona uma classe que contém campos que exigem objetos Codec
correspondentes, você precisa garantir que você instancia os objetos Codec
para os campos de classe antes de instanciar o Codec
para a classe. Você pode utilizar o parâmetro CodecRegistry
no método get()
para passar qualquer uma das instâncias do Codec
em que o Codec
confia.
O exemplo de código a seguir mostra como você pode implementar CodecProvider
para passar ao MonolightCodec
quaisquer instâncias Codec
necessárias em uma instância CodecRegistry
, como o PowerStatusCodec
do nosso exemplo 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 visualizar um exemplo executável que demonstra operações de leitura e gravação utilizando estas classes do Codec
, consulte a seção Exemplo de codec personalizado deste guia.
Ao trabalhar com POJOs, considere utilizar o PojoCodecProvider
para minimizar o código duplicado para converter tipos de dados comumente utilizados e personalizar seu comportamento. Consulte nosso guia de personalização do POJO para obter mais informações.
Registro Codec padrão
O registro de codec padrão é um definir de classes CodecProvider
que especificam a conversão entre tipos Java e MongoDB comumente usados. O driver usa automaticamente o registro de codec padrão, a menos que você especifique um registro diferente.
Se for necessário substituir o comportamento de uma ou mais classes Codec
, mas manter o comportamento do registro de codecs padrão para as outras classes, você poderá especificar todos os registros em ordem de precedência. Por exemplo, se você quiser substituir o comportamento do provedor padrão de um Codec
para tipos de enum pelo seu MyEnumCodec
personalizado, deverá adicioná-lo à lista de registros antes do registro de codecs padrão, conforme mostrado no exemplo abaixo:
CodecRegistry newRegistry = CodecRegistries.fromRegistries( CodecRegistries.fromCodecs(new MyEnumCodec()), MongoClientSettings.getDefaultCodecRegistry());
Para obter mais informações sobre as classes e interfaces nesta seção, consulte as seguintes seções de documentação da API:
BsonTypeClassMap
A classe BsonTypeClassMap
contém um mapeamento recomendado entre os tipos BSON e Java. Você pode utilizar esta classe em seu Codec
ou CodecProvider
personalizado para ajudá-lo a gerenciar quais tipos de Java para decodificar seus tipos de BSON para contêiner classes que implementam Iterable
ou Map
como a classe Document
.
Você pode adicionar ou modificar o mapeamento padrão do BsonTypeClassMap
passando um Map
contendo entradas novas ou de substituição.
O seguinte trecho de código mostra como você pode recuperar o tipo de classe Java que corresponde ao tipo BSON na instância BsonTypeClassMap
padrão:
BsonTypeClassMap bsonTypeClassMap = new BsonTypeClassMap(); Class<?> clazz = bsonTypeClassMap.get(BsonType.ARRAY); System.out.println("Java type: " + clazz.getName());
Esse código gera o seguinte:
Java type: java.util.List
Você pode modificar esses mapeamentos na sua instância especificando substituições no construtor BsonTypeClassMap
. O snippet de código a seguir mostra como você pode substituir o mapeamento de ARRAY
em sua instância BsonTypeClassMap
pela classe 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());
Esse código gera o seguinte:
Java type: java.util.Set
Para obter uma lista completa dos mapeamentos padrão, consulte a documentação da API BsonTypeClassMap.
Para um exemplo de como a classe Document
utiliza o BsonTypeClassMap
, consulte o código-fonte do driver para as seguintes classes:
Exemplo de codec personalizado
Nesta seção, mostramos como você pode implementar Codec
e CodecProvider
para definir a lógica de codificação e decodificação de uma classe Java personalizada. Também mostramos como você pode especificar e usar suas implementações personalizadas para realizar operações de inserção e recuperação.
O trecho de código a seguir mostra nosso exemplo de classe personalizada chamada Monolight
e seus campos que queremos armazenar e recuperar de uma coleção do MongoDB:
public class Monolight { private PowerStatus powerStatus = PowerStatus.OFF; private Integer colorTemperature; public Monolight() {} // ...
Esta classe contém os seguintes campos, cada um dos quais precisamos atribuir um Codec
:
powerStatus
descreve se a luz está ligada ou "desligada" para a qual usamos o PowerStatusCodec que converte valores de enumeração específicos em booleanos BSON.colorTemperature
descreve a cor da luz e contém um valorInteger
para o qual usamos oIntegerCodec
incluído na biblioteca BSON.
O seguinte exemplo de código mostra como podemos implementar um Codec
para a classe Monolight
. Observe que o construtor espera uma instância de CodecRegistry
da qual ele recupera as instâncias de Codec
necessárias para codificar e decodificar seus 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 garantir que disponibilizaremos as instâncias Codec
dos campos para Monolight
, implementamos um CodecProvider
personalizado mostrado no exemplo de código a seguir:
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; } }
Após definir a lógica de conversão, podemos realizar o seguinte:
Armazenar dados de instâncias de
Monolight
no MongoDBRecuperar dados do MongoDB em instâncias de
Monolight
A classe de exemplo a seguir contém código que atribui o MonolightCodecProvider
à instância MongoCollection
passando-o para o método withCodecRegistry()
. A classe de exemplo também insere e recupera dados utilizando a classe Monolight
e codecs associados:
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); } } }
Se você executar o exemplo anterior, verá a seguinte saída:
[Monolight [powerStatus=ON, colorTemperature=5200]]
Para obter mais informações sobre os métodos e as classes mencionadas nesta seção, consulte a seguinte documentação da API: