Menu Docs
Página inicial do Docs
/ / /
Java síncrono
/ /

Codecs

Nesta página

  • Visão geral
  • Codec
  • CodecRegistry
  • CodecProvider
  • Registro Codec padrão
  • BsonTypeClassMap
  • Exemplo de codec personalizado

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.

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
writer
Uma instância de uma classe que implementa o BsonWriter, um tipo de interface que expõe métodos para escrever um documento BSON. Por exemplo, a implementação do BsonBinaryWriter grava em um fluxo binário de dados. Use esta instância para escrever seu valor BSON usando o método de escrita apropriado.
value
Os dados que sua implementação codifica. O tipo deve corresponder à variável de tipo atribuída à sua implementação.
encoderContext
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
bsonReader
Uma instância de uma classe que implementa o BsonReader, um tipo de interface que expõe métodos para ler um documento BSON. Por exemplo, a implementação do BsonBinaryReader lê a partir de um fluxo binário de dados.
decoderContext
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> {
@Override
public void encode(BsonWriter writer, PowerStatus value, EncoderContext encoderContext) {
if (value != null) {
writer.writeBoolean(value.equals(PowerStatus.ON) ? Boolean.TRUE : Boolean.FALSE);
}
}
@Override
public PowerStatus decode(BsonReader reader, DecoderContext decoderContext) {
return reader.readBoolean() ? PowerStatus.ON : PowerStatus.OFF;
}
@Override
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 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, um Codec que converte Integers 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<String> powerStatusCodec = codecRegistry.get(String.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:

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() {}
@Override
@SuppressWarnings("unchecked")
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.

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:

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:

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 valor Integer para o qual usamos o IntegerCodec 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);
}
@Override
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();
}
@Override
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;
}
@Override
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() {}
@Override
@SuppressWarnings("unchecked")
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 MongoDB

  • Recuperar 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:

Voltar

Personalização POJO