Menu Docs
Página inicial do Docs
/ / /
Driver de Kotlin Sync
/

Codecs

Nesta página

  • Visão geral
  • Codec
  • Método de codificação()
  • Método decode()
  • Exemplos
  • CodecRegistry
  • CodecProvider
  • Registro Codec padrão
  • BsonTypeClassMap
  • Exemplo de codec personalizado

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:

  • Codec

  • CodecRegistry

  • CodecProvider

  • Exemplo de codec personalizado

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 .

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().

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.

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.

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:

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 de Codec

  • fromProviders(): Cria um registro a partir de instâncias de CodecProvider

  • fromRegistries(): Cria um registro a partir de instâncias de CodecRegistry

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 converte Integers. Faz parte do pacote BSON.

  • PowerStatusCodec: Exemplo Codec 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:

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.

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:

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.

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 do PowerStatus para booleanos BSON.

  • colorTemperature: descreve a cor da luz do dispositivo em celvins como um valor Int . Para este campo, utilize o IntegerCodec 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 {
@Suppress("UNCHECKED_CAST")
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 MongoDB

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

Voltar

Serialização Kotlin