Overview
このガイドでは、 Kotlin オブジェクトと BSON データのエンコードとデコードを処理するCodecとサポート クラスについて学習できます。 Codec抽象化を使用すると、任意の Kotlin 型を対応する BSON 型にマップできます。 この機能を使用すると、 DocumentやBsonDocumentなどのクラスを使用する代わりに、ドメイン オブジェクトを BSON との間で直接マッピングできます。
次のセクションでは、Codec 抽象化を使用してカスタム エンコードおよびデコード ロジックを指定する方法と、実装例を確認する方法について説明します。
Tip
Kotlin 直列化
Kotlin直列化では、カスタム コーデックを実装する代わりに @Serializable クラスを使用してデータのエンコードとデコードを処理できます。kotlinx.serialization ライブラリにすでに知識がある場合、または慣用的なKotlinアプローチを使用したい場合は、 Kotlin直列化を選択することをお勧めします。詳しくは、データ直列化ガイドをご覧ください。
コーデック
Codecインターフェースには、 Kotlin オブジェクトを BSON データにシリアル化および逆シリアル化するための抽象メソッドが含まれています。 このインターフェースの実装では、カスタム変換ロジックを定義できます。
Codec インターフェースを実装するには、encode()、decode()、および getEncoderClass() 抽象メソッドをオーバーライドします。
encode() メソッド
encode() メソッドには次のパラメーターが必要です。
Parameter Type | 説明 |
|---|---|
| BSON ドキュメントを書込むためのメソッドを公開するインターフェース型である |
| 実装によってエンコードされるデータ。型は、実装に割り当てられた型変数と一致する必要があります。 |
| 現在の値を MongoDB コレクションに保存するかどうかなど、BSON にエンコードされる Kotlin オブジェクト データに関するメタ情報が含まれます。 |
encode()メソッドはBsonWriterインスタンスを使用してエンコードされた値を MongoDB に送信し、値を返しません。
デコード() メソッド
decode()メソッドは、BSON データから取得した値が設定された Kotlin オブジェクト インスタンスを返します。 このメソッドには次のパラメータが必要です。
Parameter Type | 説明 |
|---|---|
| BSON ドキュメントを読み取るためのメソッドを公開するインターフェース型である |
| 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 }
PowerStatusCodecCodecRegistryのインスタンスを に追加できます。レジストリに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:CodecIntegersを変換するこれは、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を使用します。
次のコードは、Monolightクラスに Codec を実装する方法を示しています。 コンストラクターは、クラスフィールドのエンコードとデコードに必要な Codec インスタンスを検索する CodecRegistry のインスタンスを期待しています。
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 ドキュメントを参照してください。