Codecs
Nesta página
Visão geral
Neste guia, você pode aprender sobre Codecs e classes de suporte que lidam com a codificação e decodificação de objetos Kotlin de e para dados BSON. A abstração do Codec
permite mapear qualquer tipo Kotlin para um tipo BSON correspondente. Você pode usar esse recurso para mapear seus objetos de domínio diretamente de e para o BSON, em vez de usar classes 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:
Dica
Serialização Kotlin
Você pode usar a serialização do Kotlin para lidar com a codificação e decodificação de dados com classes @Serializable
em vez de implementar codecs personalizados. Você pode escolher a serialização do Kotlin se já estiver familiarizado com a biblioteca kotlinx.serialization
ou preferir usar uma abordagem idiomática do Kotlin . Para saber mais, consulte o guia serialização de Kotlin .
Codec
A interface do Codec
contém métodos abstratos para serializar e desserializar objetos Kotlin para dados BSON. Você pode definir a lógica de conversão personalizada em sua implementação desta interface.
Para implementar a interface do Codec
, substitua os métodos abstratos encode()
, decode()
e getEncoderClass()
.
Método de codificação()
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 metainformações sobre os dados de objeto Kotlin que codifica para BSON, incluindo se deve armazenar o valor atual em uma coleção MongoDB . |
O método encode()
utiliza a instância BsonWriter
para enviar o valor codificado para MongoDB e não retorna um valor.
Método decode()
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 |
---|---|
reader | 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.
Exemplos
Esta seção contém exemplos de código que mostram como você pode implementar uma interface Codec
personalizada.
O enumeração PowerStatus
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 a interface Codec
para converter os valores Kotlin enum
em valores booleanos BSON correspondentes. O método encode()
converte um valor 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 de PowerStatusCodec
ao seu CodecRegistry
. Consulte a seção CodecRegistry desta página para saber como incluir seu Codec
em seu registro.
Para saber mais sobre as classes e interfaces mencionadas nesta seção, consulte a seguinte documentação da API:
CodecRegistry
Um CodecRegistry
é uma coleção imutável de instâncias do Codec
que codificam e decodificam classes Kotlin . 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()
: Cria um registro a partir de instâncias deCodec
fromProviders()
: Cria um registro a partir de instâncias deCodecProvider
fromRegistries()
: Cria um registro a partir de instâncias deCodecRegistry
O seguinte código mostra como construir um CodecRegistry
utilizando o método fromCodecs()
:
val codecRegistry = CodecRegistries .fromCodecs(IntegerCodec(), PowerStatusCodec())
O exemplo anterior atribui ao CodecRegistry
as seguintes implementações do Codec
:
IntegerCodec
:Codec
que converteIntegers
. Faz parte do pacote BSON.PowerStatusCodec
: ExemploCodec
da seção anterior que converte valores de enumeração do Kotlin em booleanos BSON.
Você pode recuperar as instâncias Codec
da instância CodecRegistry
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 codecRegistry.get()
gerará 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 para criar instâncias do Codec
e atribuí-las a instâncias do CodecRegistry
. Semelhante à interface CodecRegistry
, a biblioteca BSON usa as instâncias do Codec
recuperadas pelo método CodecProvider.get()
para converter entre Kotlin e tipos de dados BSON.
No entanto, nos casos em que você adicionar uma classe que contém campos que exigem objetos Codec
correspondentes, certifique-se de instanciar os objetos Codec
para os campos da classe antes de instanciar o Codec
para a classe inteira. Você pode utilizar o parâmetro CodecRegistry
no método CodecProvider.get()
para passar qualquer uma das instâncias do Codec
em que o Codec
confia.
Para visualizar um exemplo executável que demonstra operações de leitura e escrita utilizando classes 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 objetos Kotlin comumente usados e tipos MongoDB . O driver usa automaticamente o registro de codec padrão, a menos que você especifique um registro diferente.
Para substituir o comportamento de uma ou mais classes Codec
, mas manter o comportamento do registro de codecs padrão para as outras classes, você pode especificar os registros em ordem de precedência. Por exemplo, suponha que você queira substituir o comportamento padrão do provedor de um Codec
para tipos de enumeração pelo seu MyEnumCodec
personalizado . Você deve adicioná-lo à lista de registros em uma posição antes do registro de codec padrão, conforme mostrado no exemplo a seguir:
val newRegistry = CodecRegistries.fromRegistries( CodecRegistries.fromCodecs(MyEnumCodec()), MongoClientSettings.getCodecRegistry() )
Para obter mais informações sobre as classes e interfaces nesta seção, consulte a seguinte 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 para quais tipos de Kotlin decodificar seus BSON types . Ele também contém classes de container 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 código a seguir mostra como recuperar o tipo de classe Kotlin que corresponde ao tipo de array BSON na instância BsonTypeClassMap
padrão:
val bsonTypeClassMap = BsonTypeClassMap() val clazz = bsonTypeClassMap[BsonType.ARRAY] println("Kotlin class name: " + clazz.name)
Kotlin class name: 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 do tipo array BSON em sua BsonTypeClassMap
instância pela Set
classe :
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)
Kotlin class name: java.util.Set
Para obter uma lista completa dos mapeamentos padrão, consulte o BsonTypeClassMap Documentação da API.
Exemplo de codec personalizado
Esta seção demonstra como você pode implementar interfaces do Codec
e CodecProvider
para definir a lógica de codificação e decodificação de uma classe Kotlin personalizada. Ele mostra como você pode especificar e usar suas implementações personalizadas para realizar operações de leitura e escrita.
O seguinte código define a classe de dados de amostra Monolight
:
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 que cada um exige um Codec
correspondente para lidar com a codificação e decodificação:
powerStatus
: descreve se a luz do dispositivo é"ON"
ou"OFF"
. Para este campo, use o PowerStatusCodec que converte os valores de enumeração doPowerStatus
para booleanos BSON.colorTemperature
: descreve a cor da luz do dispositivo em celvins como um valorInt
. Para este campo, utilize oIntegerCodec
fornecido na biblioteca BSON.
O código a seguir mostra como implementar um Codec
para a classe Monolight
. O construtor espera uma instância de CodecRegistry
da qual ele recupera as instâncias Codec
necessárias para codificar e decodificar os campos de classe :
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 as instâncias Codec
dos campos estejam disponíveis para a classe Monolight
, implemente um CodecProvider
personalizado conforme 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, você pode executar as seguintes ações:
Armazenar instâncias de
Monolight
no MongoDBRecuperar documentos do MongoDB como instâncias de
Monolight
O código a seguir 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
:
val mongoClient = MongoClient.create(uri) val codecRegistry = CodecRegistries.fromRegistries( CodecRegistries.fromCodecs(IntegerCodec(), PowerStatusCodec()), CodecRegistries.fromProviders(MonolightCodecProvider()), MongoClientSettings.getDefaultCodecRegistry() ) val database = mongoClient.getDatabase("codec_test") val collection = database.getCollection<Monolight>("monolights") .withCodecRegistry(codecRegistry) // Insert instances of Monolight val monolights = listOf( Monolight(PowerStatus.ON, 5200), Monolight(PowerStatus.OFF, 3000) ) collection.insertMany(monolights) // Retrieve instances of Monolight val results = collection.find() results.forEach { l -> println(l) }
Monolight { powerStatus: ON, colorTemperature: 5200 } Monolight { powerStatus: OFF, colorTemperature: 3000 }
Para obter mais informações sobre os métodos e as classes mencionadas nesta seção, consulte a seguinte documentação da API: