コーデック
項目一覧
Overview
このガイドでは、 Kotlin オブジェクトと BSON データのエンコードとデコードを処理するCodecとサポート クラスについて学習できます。 Codec
抽象化を使用すると、任意の Kotlin 型を対応する BSON 型にマップできます。 この機能を使用すると、 Document
やBsonDocument
などのクラスを使用する代わりに、ドメイン オブジェクトを BSON との間で直接マッピングできます。
次のセクションでは、Codec
抽象化を使用してカスタム エンコードおよびデコード ロジックを指定する方法と、実装例を確認する方法について説明します。
Tip
Kotlin 直列化
Kotlin シリアル化では、カスタム コーデックを実装する代わりに@Serializable
クラスを使用してデータのエンコードとデコードを処理できます。 kotlinx.serialization
ライブラリにすでに知識がある場合、または慣用的な Kotlin アプローチを使用したい場合は、 Kotlin シリアル化を選択することをお勧めします。 詳しくは、 Kotlin 直列化のガイドを参照してください。
コーデック
Codec
インターフェースには、 Kotlin オブジェクトを BSON データにシリアル化および逆シリアル化するための抽象メソッドが含まれています。 このインターフェースの実装では、カスタム変換ロジックを定義できます。
Codec
インターフェースを実装するには、encode()
、decode()
、および getEncoderClass()
抽象メソッドをオーバーライドします。
encode() メソッド
encode()
メソッドには次のパラメーターが必要です。
Parameter Type | 説明 |
---|---|
writer | BSON ドキュメントを書込むためのメソッドを公開するインターフェース型である BsonWriter を実装するクラスのインスタンス。たとえば、BsonBinaryWriter 実装はバイナリ ストリームのデータに書込みます。このインスタンスを使用して、適切な書込みメソッドで BSON 値を書込みます。 |
value | 実装によってエンコードされるデータ。型は、実装に割り当てられた型変数と一致する必要があります。 |
encoderContext | 現在の値を MongoDB コレクションに保存するかどうかなど、BSON にエンコードされる Kotlin オブジェクト データに関するメタ情報が含まれます。 |
encode()
メソッドはBsonWriter
インスタンスを使用してエンコードされた値を MongoDB に送信し、値を返しません。
デコード() メソッド
decode()
メソッドは、BSON データから取得した値が設定された Kotlin オブジェクト インスタンスを返します。 このメソッドには次のパラメータが必要です。
Parameter Type | 説明 |
---|---|
reader | BSON ドキュメントを読み取るためのメソッドを公開するインターフェース型である BsonReader を実装するクラスのインスタンス。たとえば、BsonBinaryReader 実装はバイナリ ストリームのデータから読み取ります。 |
decoderContext | Kotlin オブジェクトにデコードされる BSON データに関する情報が含まれます。 |
Kotlin は型消去により型を推論できないため、 getEncoderClass()
メソッドは Kotlin クラスのクラス インスタンスを返します。
例
このセクションでは、カスタムCodec
インターフェースを実装する方法を示すコード例が含まれています。
PowerStatus
列挙型には、電気スイッチの状態を表す値"ON"
と"OFF"
が含まれています。
enum class PowerStatus { ON, OFF }
PowerStatusCodec
クラスは、 Kotlin enum
値を対応する BSON ブール値に変換するためのCodec
インターフェースを実装します。 encode()
メソッドはPowerStatus
値を BSON ブール値に変換し、 decode()
メソッドは逆方向の変換を実行します。
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 }
PowerStatusCodec
CodecRegistry
のインスタンスを に追加できます。レジストリにCodec
を含める方法については、このページの [ CodecRegistry]セクションを表示してください。
このセクションで説明されるクラスとインターフェースの詳細については、次の API ドキュメントを参照してください。
CodecRegistry
CodecRegistry
は、 Kotlin クラスをエンコードおよびデコードするCodec
インスタンスの不変のコレクションです。 次のCodecRegistries
クラスの静的ファクトリー メソッドのいずれかを使用して、関連付けられた型に含まれるCodec
インスタンスからCodecRegistry
を構築できます。
fromCodecs()
:Codec
インスタンスからレジストリを作成しますfromProviders()
:CodecProvider
インスタンスからレジストリを作成しますfromRegistries()
:CodecRegistry
インスタンスからレジストリを作成します
次のコードは、 fromCodecs()
メソッドを使用してCodecRegistry
を構築する方法を示しています。
val codecRegistry = CodecRegistries .fromCodecs(IntegerCodec(), PowerStatusCodec())
上記の例では、 CodecRegistry
に次のCodec
実装が割り当てられています。
IntegerCodec
:Codec
Integers
を変換するこれは、BSON パッケージの一部です。PowerStatusCodec
: Kotlin 列挙値を BSON ブール値に変換する前のセクションのサンプルCodec
。
次のコードを使用して、 CodecRegistry
インスタンスからCodec
インスタンスを取得できます。
val powerStatusCodec = codecRegistry.get(PowerStatus::class.java) val integerCodec = codecRegistry.get(Integer::class.java)
登録されていないクラスのCodec
インスタンスを検索しようとすると、 codecRegistry.get()
メソッドはCodecConfigurationException
例外を発生させます。
このセクションのクラスとインターフェースの詳細については、次の API ドキュメントを参照してください。
CodecProvider
CodecProvider
は、 Codec
インスタンスを作成し、それをCodecRegistry
インスタンスに割り当てる抽象メソッドを含むインターフェースです。 CodecRegistry
インターフェースと同様に、BSON ライブラリはCodecProvider.get()
メソッドによって検索されたCodec
インスタンスを使用して、 Kotlin と BSON データ型間の変換を行います。
ただし、対応するCodec
オブジェクトを必要とするフィールドを含むクラスを追加する場合は、クラス全体のCodec
をインスタンス化する前に、クラスのフィールドのCodec
オブジェクトをインスタンス化してください。 CodecProvider.get()
メソッドのCodecRegistry
パラメータを使用して、 Codec
が依存するCodec
インスタンスのいずれかを渡すことができます。
Codec
クラスを使用した読み取りおよび書込み (write) 操作を示す実行可能な例については、このガイドの「カスタム コーデックの例」セクションを参照してください。
Default Codec Registry
デフォルトのコーデック レジストリは、一般的に使用される Kotlin オブジェクトと MongoDB 型間の変換を指定するCodecProvider
クラスのセットです。 別のコーデック レジストリを指定しない限り、ドライバーは自動的にデフォルトのコーデック レジストリを使用します。
1 つ以上のCodec
クラスの動作をオーバーライドするが、他のクラスのデフォルトのコーデック レジストリの動作を維持するには、優先順位に従ってレジストリを指定します。 たとえば、列挙型のCodec
のデフォルトのプロバイダー動作をカスタムMyEnumCodec
でオーバーライドしたいとします。 次の例に示すように、デフォルトのコーデック レジストリの前の位置でレジストリ リストに追加する必要があります。
val newRegistry = CodecRegistries.fromRegistries( CodecRegistries.fromCodecs(MyEnumCodec()), MongoClientSettings.getCodecRegistry() )
このセクションのクラスとインターフェースの詳細については、次の API ドキュメントを参照してください。
BsonTypeClassMap
BsonTypeClassMap
クラスには、BSON 型と Kotlin 型間の推奨マッピングが含まれています。 このクラスをカスタムCodec
またはCodecProvider
で使用すると、Kotlin をデコードするBSON types 型を管理するのに役立ちます。また、 Document
クラスなど、 Iterable
またはMap
を実装するコンテナ クラスも含まれています。
新しいエントリまたは置換エントリを含む Map
を渡すことにより、BsonTypeClassMap
のデフォルト マッピングを追加または変更できます。
次のコードは、デフォルトのBsonTypeClassMap
インスタンス内の BSON 配列型に対応する Kotlin クラス型を検索する方法を示しています。
val bsonTypeClassMap = BsonTypeClassMap() val clazz = bsonTypeClassMap[BsonType.ARRAY] println("Kotlin class name: " + clazz.name)
Kotlin class name: java.util.List
BsonTypeClassMap
コンストラクターで置換を指定することにより、インスタンス内のこれらのマッピングを変更できます。 次のコード スニペットは、 BsonTypeClassMap
インスタンス内の BSON 配列型のマッピングを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)
Kotlin class name: java.util.Set
デフォルトのマッピングの完全なリストについては、 BsonTypeClassMap を参照してください API ドキュメント。
カスタム Codec の例
このセクションでは、Codec
CodecProvider
インターフェースと インターフェースを実装して、カスタム Kotlin クラスのエンコードおよびデコード ロジックを定義する方法を示します。カスタム実装を指定して使用し、読み取りおよび書込み操作を実行する方法を示します。
次のコードは、サンプル データ クラスMonolight
を定義します。
data class Monolight( var powerStatus: PowerStatus = PowerStatus.OFF, var colorTemperature: Int? = null ) { override fun toString(): String = "Monolight { powerStatus: $powerStatus, colorTemperature: $colorTemperature }" }
このクラスには次のフィールドが含まれており、エンコードとデコードを処理するために対応するCodec
がそれぞれに必要です。
powerStatus
: デバイスのライトが"ON"
であるか、"OFF"
であるかを表します。 このフィールドには、PowerStatus
列挙値を BSON ブール値に変換するPowerStatusCodecを使用します。colorTemperature
: デバイスライトの色をキロメートル単位で記述し、Int
値として記述します。 このフィールドには、BSON ライブラリで提供されているIntegerCodec
を使用します。
The following code shows how to implement a Codec
for the Monolight
class. The constructor expects an instance of CodecRegistry
from which it retrieves the Codec
instances needed to encode and decode the class fields:
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 }
フィールドのCodec
インスタンスがMonolight
クラスで使用できるようにするには、次のコード例に示すようにカスタムCodecProvider
を実装します。
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 } }
変換ロジックを定義した後、次のアクションを実行できます。
MongoDB で
Monolight
のインスタンスを保存する次のインスタンスとして MongoDB からドキュメントを取得:
Monolight
次のコードでは、 MonolightCodecProvider
をwithCodecRegistry()
メソッドに渡してMongoCollection
インスタンスに割り当てます。 このサンプル クラスでは、 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 }
このセクションで説明されるメソッドとクラスの詳細については、次の API ドキュメントを参照してください。