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 Kotlin de e para dados BSON no driver Kotlin do MongoDB. A abstração Codec
permite mapear qualquer tipo de Kotlin para um tipo de BSON correspondente. Você pode usar isso para mapear seus objetos de domínio diretamente de e para o BSON, em vez de usar classes de dados ou 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:
Codec
A interface do Codec
contém métodos abstratos para serializar e desserializar objetos Kotlin para dados BSON. Você pode definir sua lógica de conversão entre o BSON e seu objeto Kotlin 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 Kotlin que ele 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 Kotlin 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 Kotlin. |
O método getEncoderClass()
retorna uma instância de classe Kotlin uma vez que Kotlin 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.
enum class PowerStatus { ON, OFF }
A classe PowerStatusCodec
implementa Codec
para converter os valores Kotlin 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.
class PowerStatusCodec : Codec<PowerStatus> { override fun encode(writer: BsonWriter, value: PowerStatus, encoderContext: EncoderContext) = writer.writeBoolean(value == PowerStatus.ON) override fun decode(reader: BsonReader, decoderContext: DecoderContext): PowerStatus { return when (reader.readBoolean()) { true -> PowerStatus.ON false -> PowerStatus.OFF } } override fun getEncoderClass(): Class<PowerStatus> = PowerStatus::class.java }
Você pode adicionar uma instância do PowerStatusCodec
ao seu CodecRegistry
que contém um mapeamento entre seu Codec
e o tipo de objeto Kotlin ao qual ele se aplica. Continue para a seção CodecRegistry desta página para ver como você pode 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 Kotlin 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()
:
val codecRegistry = CodecRegistries.fromCodecs(IntegerCodec(), 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 valores enum Kotlin em booleanos BSON.
Você pode recuperar as instâncias Codec
da instância CodecRegistry
do exemplo anterior usando o código a seguir:
val powerStatusCodec = codecRegistry.get(PowerStatus::class.java) val integerCodec = codecRegistry.get(Integer::class.java)
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 usa as instâncias do Codec
recuperadas pelo método get()
para converter entre Kotlin 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 da 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:
class MonolightCodec(registry: CodecRegistry) : Codec<Monolight> { private val powerStatusCodec: Codec<PowerStatus> private val integerCodec: Codec<Int> init { powerStatusCodec = registry[PowerStatus::class.java] integerCodec = IntegerCodec() } override fun encode(writer: BsonWriter, value: Monolight, encoderContext: EncoderContext) { writer.writeStartDocument() writer.writeName("powerStatus") powerStatusCodec.encode(writer, value.powerStatus, encoderContext) writer.writeName("colorTemperature") integerCodec.encode(writer, value.colorTemperature, encoderContext) writer.writeEndDocument() } override fun decode(reader: BsonReader, decoderContext: DecoderContext): Monolight { val monolight = Monolight() reader.readStartDocument() while (reader.readBsonType() != BsonType.END_OF_DOCUMENT) { when (reader.readName()) { "powerStatus" -> monolight.powerStatus = powerStatusCodec.decode(reader, decoderContext) "colorTemperature" -> monolight.colorTemperature = integerCodec.decode(reader, decoderContext) "_id" -> reader.readObjectId() } } reader.readEndDocument() return monolight } override fun getEncoderClass(): Class<Monolight> = Monolight::class.java }
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.
Registro Codec padrão
O registro de codec padrão é um definir de classes CodecProvider
que especificam a conversão entre tipos Kotlin 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:
val newRegistry = CodecRegistries.fromRegistries( CodecRegistries.fromCodecs(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 Kotlin. Você pode usar essa classe em seu Codec
ou CodecProvider
personalizado para ajudá-lo a gerenciar quais tipos de Kotlin decodificam seus BSON types para classes de contêiner 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 Kotlin que corresponde ao tipo BSON na instância BsonTypeClassMap
padrão:
val bsonTypeClassMap = BsonTypeClassMap() val clazz = bsonTypeClassMap[BsonType.ARRAY] println("Class name: " + clazz.name)
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
:
val replacements = mutableMapOf<BsonType, Class<*>>(BsonType.ARRAY to MutableSet::class.java) val bsonTypeClassMap = BsonTypeClassMap(replacements) val clazz = bsonTypeClassMap[BsonType.ARRAY] println("Class name: " + clazz.name)
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 Kotlin personalizada. Também mostramos como você pode especificar e usar suas implementações personalizadas para realizar operações de inserção e recuperação.
Dica
Serialização Kotlin
Como alternativa à implementação de codecs personalizados, você pode usar a serialização do Kotlin para lidar com a codificação e a decodificação de dados com classes @Serializable
. Você pode escolher a serialização do Kotlin se já estiver familiarizado com a estrutura ou preferir usar uma abordagem idiomática do Kotlin . Consulte a documentação do Kotlin Serialization para obter mais informações.
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:
data class Monolight( var powerStatus: PowerStatus = PowerStatus.OFF, var colorTemperature: Int? = null ) { override fun toString(): String = "Monolight [powerStatus=$powerStatus, colorTemperature=$colorTemperature]" }
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 valorInt
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:
class MonolightCodec(registry: CodecRegistry) : Codec<Monolight> { private val powerStatusCodec: Codec<PowerStatus> private val integerCodec: Codec<Int> init { powerStatusCodec = registry[PowerStatus::class.java] integerCodec = IntegerCodec() } override fun encode(writer: BsonWriter, value: Monolight, encoderContext: EncoderContext) { writer.writeStartDocument() writer.writeName("powerStatus") powerStatusCodec.encode(writer, value.powerStatus, encoderContext) writer.writeName("colorTemperature") integerCodec.encode(writer, value.colorTemperature, encoderContext) writer.writeEndDocument() } override fun decode(reader: BsonReader, decoderContext: DecoderContext): Monolight { val monolight = Monolight() reader.readStartDocument() while (reader.readBsonType() != BsonType.END_OF_DOCUMENT) { when (reader.readName()) { "powerStatus" -> monolight.powerStatus = powerStatusCodec.decode(reader, decoderContext) "colorTemperature" -> monolight.colorTemperature = integerCodec.decode(reader, decoderContext) "_id" -> reader.readObjectId() } } reader.readEndDocument() return monolight } override fun getEncoderClass(): Class<Monolight> = Monolight::class.java }
Para garantir que disponibilizaremos as instâncias Codec
dos campos para Monolight
, implementamos um CodecProvider
personalizado mostrado no exemplo de código a seguir:
class MonolightCodecProvider : CodecProvider { override fun <T> get(clazz: Class<T>, registry: CodecRegistry): Codec<T>? { return if (clazz == Monolight::class.java) { MonolightCodec(registry) as Codec<T> } else null // Return null when not a provider for the requested class } }
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:
fun main() = runBlocking { val mongoClient = MongoClient.create("<connection string uri>") val codecRegistry = CodecRegistries.fromRegistries( CodecRegistries.fromCodecs(IntegerCodec(), PowerStatusCodec()), CodecRegistries.fromProviders(MonolightCodecProvider()), MongoClientSettings.getDefaultCodecRegistry() ) val database = mongoClient.getDatabase("codecs_example_products") val collection = database.getCollection<Monolight>("monolights") .withCodecRegistry(codecRegistry) // Construct and insert an instance of Monolight val myMonolight = Monolight(PowerStatus.ON, 5200) collection.insertOne(myMonolight) // Retrieve one or more instances of Monolight val lights = collection.find().toList() println(lights) }
[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: