コーデック
Overview
このガイドでは、MongoDB Kotlin ドライバーで Kotlin オブジェクトと BSON データのエンコードとデコードを処理するCodecとサポート クラスについて学習できます。 Codec
抽象化を使用すると、任意の Kotlin 型を対応する BSON 型にマップできます。 これを使用すると、データ クラスや中間マップベースのオブジェクト(例: Document
やBsonDocument
など)を使用する代わりに、ドメイン オブジェクトを BSON との間で直接マップできます。
次のセクションでは、Codec
抽象化を使用してカスタム エンコードおよびデコード ロジックを指定する方法と、実装例を確認する方法について説明します。
コーデック
Codec
インターフェースには、 Kotlin オブジェクトを BSON データにシリアル化および逆シリアル化するための抽象メソッドが含まれています。 このインターフェースの実装では、BSON と Kotlin オブジェクト間の変換ロジックを定義できます。
Codec
インターフェースを実装するには、encode()
、decode()
、および getEncoderClass()
抽象メソッドをオーバーライドします。
encode()
メソッドには次のパラメーターが必要です。
Parameter Type | 説明 |
---|---|
| BSON ドキュメントを書込むためのメソッドを公開するインターフェース型である |
| 実装によってエンコードされるデータ。型は、実装に割り当てられた型変数と一致する必要があります。 |
| 現在の値を MongoDB コレクションに保存するかどうかなど、BSON にエンコードされる Kotlin オブジェクト データに関するメタ情報が含まれます。 |
このメソッドは、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 }
Codec
とそれが適用される Kotlin オブジェクトタイプとの間のマッピングを含むPowerStatusCodec
のインスタンスをCodecRegistry
に追加できます。 このページの「 CodecRegistry」セクションに進み、 Codec
を含める方法を確認します。
このセクションのクラスとインターフェースの詳細については、次の API ドキュメントを参照してください。
CodecRegistry
CodecRegistry
は、指定された Kotlin クラスをエンコードおよびデコードするCodec
インスタンスの不変のコレクションです。 次のCodecRegistries
クラスの静的ファクトリー メソッドのいずれかを使用して、関連付けられた型に含まれるCodec
インスタンスからCodecRegistry
を構築できます。
fromCodecs()
fromProviders()
fromRegistries()
次のコード スニペットは、fromCodecs()
メソッドを使用して CodecRegistry
を構築する方法を示しています。
val codecRegistry = CodecRegistries.fromCodecs(IntegerCodec(), PowerStatusCodec())
前の例では、CodecRegistry
に次の Codec
実装を割り当てています。
IntegerCodec
、Integers
を変換し、BSON パッケージの一部となるCodec
です。PowerStatusCodec : Kotlin 列挙値を BSON ブール値に変換するサンプル
Codec
次のコードを使用して、前の例の CodecRegistry
インスタンスから Codec
インスタンスを検索できます。
val powerStatusCodec = codecRegistry.get(PowerStatus::class.java) val integerCodec = codecRegistry.get(Integer::class.java)
登録されていないクラスの Codec
インスタンスを検索しようとすると、get()
メソッドは CodecConfigurationException
の例外をスローします。
このセクションのクラスとインターフェースの詳細については、次の API ドキュメントを参照してください。
CodecProvider
CodecProvider
は、 Codec
インスタンスを作成し、それをCodecRegistry
インスタンスに割り当てる抽象メソッドを含むインターフェースです。 CodecRegistry
と同様に、BSON ライブラリはget()
メソッドによって検索されたCodec
インスタンスを使用して、 Kotlin と BSON データ型間の変換を行います。
ただし、対応するCodec
オブジェクトを必要とするフィールドを含むクラスを追加する場合は、クラスのCodec
をインスタンス化する前に、クラスのフィールドのCodec
オブジェクトをインスタンス化する必要があります。 get()
メソッドのCodecRegistry
パラメータを使用して、 Codec
が依存するCodec
インスタンスのいずれかを渡すことができます。
次のコード例は、CodecProvider
を実装して、前の例の PowerStatusCodec
などの CodecRegistry
インスタンスで必要な Codec
インスタンスを MonolightCodec
に渡す方法を示しています。
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
クラスを使用した読み取りおよび書込み (write) 操作を示す実行可能な例については、このガイドの「カスタム コーデックの例」セクションを参照してください。
Default Codec Registry
デフォルトのコーデック レジストリは、一般的に使用される Kotlin と MongoDB 型間の変換を指定するCodecProvider
クラスのセットです。 別のコーデック レジストリを指定しない限り、ドライバーは自動的にデフォルトのコーデック レジストリを使用します。
1 つ以上の Codec
クラスの動作をオーバーライドする必要があり、他のクラスのデフォルトのコーデック レジストリの動作を維持する場合は、優先順位に従ってすべてのレジストリを指定できます。たとえば、列挙型の Codec
のデフォルトのプロバイダー動作をカスタム MyEnumCodec
でオーバーライドする場合は、次の例に示すように、デフォルトのコーデック レジストリの前にそれをレジストリ リストに追加する必要があります。
val newRegistry = CodecRegistries.fromRegistries( CodecRegistries.fromCodecs(MyEnumCodec()), MongoClientSettings.getDefaultCodecRegistry() )
このセクションのクラスとインターフェースの詳細については、次の API ドキュメントの各セクションを参照してください。
BsonTypeClassMap
BsonTypeClassMap
クラスには、BSON 型と Kotlin 型間の推奨マッピングが含まれています。 You can use this class in your custom Codec
or CodecProvider
to help you manage which Kotlin types to decode your BSON types to container classes that implement Iterable
or Map
such as the Document
class.
新しいエントリまたは置換エントリを含む Map
を渡すことにより、BsonTypeClassMap
のデフォルト マッピングを追加または変更できます。
次のコード スニペットは、デフォルトのBsonTypeClassMap
インスタンス内の BSON 型に対応する Kotlin クラスの種類を検索する方法を示しています。
val bsonTypeClassMap = BsonTypeClassMap() val clazz = bsonTypeClassMap[BsonType.ARRAY] println("Class name: " + clazz.name)
Java type: java.util.List
BsonTypeClassMap
コンストラクターで置換を指定することにより、インスタンス内のこれらのマッピングを変更できます。次のコード スニペットは、BsonTypeClassMap
インスタンス内の ARRAY
のマッピングを 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
デフォルトのマッピングの完全なリストについては、 BsonTypeClassMap を参照してください API ドキュメント。
Document
クラスが BsonTypeClassMap
を使用する方法の例については、次のクラスのドライバー ソース コードを参照してください。
カスタム Codec の例
このセクションでは、 Codec
とCodecProvider
を実装して、カスタム Kotlin クラスのエンコードおよびデコード ロジックを定義する方法を示します。 また、カスタム実装を指定して使用し、挿入操作と検索操作を実行する方法も示します。
Tip
Kotlin 直列化
カスタム コーデックを実装する代わりに、 Kotlinシリアル化を使用して、@Serializable
クラスでデータのエンコードとデコードを処理できます。 フレームワークについてすでに知っている場合、または慣用的な Kotlin アプローチを使用したい場合は、 Kotlin シリアル化を選択することをお勧めします。 詳細については、 Kotlin 直列化のドキュメントを 参照してください。
次のコード スニペットは、Monolight
というカスタム クラスの例と、MongoDB コレクションに保存および検索するフィールドを示しています。
data class Monolight( var powerStatus: PowerStatus = PowerStatus.OFF, var colorTemperature: Int? = null ) { override fun toString(): String = "Monolight [powerStatus=$powerStatus, colorTemperature=$colorTemperature]" }
このクラスには次のフィールドが含まれており、それぞれに Codec
を割り当てる必要があります。
powerStatus
ライトが"オン" に切り替えられたか"オフ" に切り替えられたかを示します。そのために、特定の列挙値を BSON ブール値に変換する PowerStatusCodec を使用します。colorTemperature
は、ライトの色を記述し、BSON ライブラリに含まれるIntegerCodec
を使用するInt
値が含まれます。
次のコード例は、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 } }
変換ロジックを定義した後、次の操作を実行できます。
Monolight
のインスタンスのデータを MongoDB に保存するMongoDB から次のインスタンスにデータを検索する
Monolight
次のサンプル クラスには、MonolightCodecProvider
を withCodecRegistry()
メソッドに渡して MongoCollection
インスタンスに割り当てるコードが含まれています。このサンプル クラスでは、Monolight
クラスと関連するコーデックを使用してデータの挿入と検索も行います。
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]]
このセクションで説明されるメソッドとクラスの詳細については、次の API ドキュメントを参照してください。