Docs Menu
Docs Home
/ / /
Kotlin コルーチン
/ /

コーデック

項目一覧

  • Overview
  • コーデック
  • CodecRegistry
  • CodecProvider
  • Default Codec Registry
  • BsonTypeClassMap
  • カスタム Codec の例

このガイドでは、MongoDB Kotlin ドライバーで Kotlin オブジェクトと BSON データのエンコードとデコードを処理するCodecとサポート クラスについて学習できます。 Codec抽象化を使用すると、任意の Kotlin 型を対応する BSON 型にマップできます。 これを使用すると、データ クラスや中間マップベースのオブジェクト(例: DocumentBsonDocumentなど)を使用する代わりに、ドメイン オブジェクトを BSON との間で直接マップできます。

次のセクションでは、Codec 抽象化を使用してカスタム エンコードおよびデコード ロジックを指定する方法と、実装例を確認する方法について説明します。

  • コーデック

  • CodecRegistry

  • CodecProvider

  • カスタム Codec の例

Codecインターフェースには、 Kotlin オブジェクトを BSON データにシリアル化および逆シリアル化するための抽象メソッドが含まれています。 このインターフェースの実装では、BSON と Kotlin オブジェクト間の変換ロジックを定義できます。

Codec インターフェースを実装するには、encode()decode()、および getEncoderClass() 抽象メソッドをオーバーライドします。

encode() メソッドには次のパラメーターが必要です。

Parameter Type
説明

writer

BSON ドキュメントを書込むためのメソッドを公開するインターフェース型である BsonWriter を実装するクラスのインスタンス。たとえば、BsonBinaryWriter 実装はバイナリ ストリームのデータに書込みます。このインスタンスを使用して、適切な書込みメソッドで BSON 値を書込みます。

value

実装によってエンコードされるデータ。型は、実装に割り当てられた型変数と一致する必要があります。

encoderContext

現在の値を MongoDB コレクションに保存するかどうかなど、BSON にエンコードされる Kotlin オブジェクト データに関するメタ情報が含まれます。

このメソッドは、BsonWriter インスタンスを使用してエンコードされた値を MongoDB に送信し、値を返しません。

decode()メソッドは、BSON データから取得した値が設定された Kotlin オブジェクト インスタンスを返します。 このメソッドには次のパラメータが必要です。

Parameter Type
説明

bsonReader

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
}

Codecとそれが適用される Kotlin オブジェクトタイプとの間のマッピングを含むPowerStatusCodecのインスタンスをCodecRegistryに追加できます。 このページの「 CodecRegistry」セクションに進み、 Codecを含める方法を確認します。

このセクションのクラスとインターフェースの詳細については、次の API ドキュメントを参照してください。

CodecRegistryは、指定された Kotlin クラスをエンコードおよびデコードするCodecインスタンスの不変のコレクションです。 次のCodecRegistriesクラスの静的ファクトリー メソッドのいずれかを使用して、関連付けられた型に含まれるCodecインスタンスからCodecRegistryを構築できます。

  • fromCodecs()

  • fromProviders()

  • fromRegistries()

次のコード スニペットは、fromCodecs() メソッドを使用して CodecRegistry を構築する方法を示しています。

val codecRegistry = CodecRegistries.fromCodecs(IntegerCodec(), PowerStatusCodec())

前の例では、CodecRegistry に次の Codec 実装を割り当てています。

  • IntegerCodecIntegersを変換し、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は、 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) 操作を示す実行可能な例については、このガイドの「カスタム コーデックの例」セクションを参照してください。

デフォルトのコーデック レジストリは、一般的に使用される Kotlin と MongoDB 型間の変換を指定するCodecProviderクラスのセットです。 別のコーデック レジストリを指定しない限り、ドライバーは自動的にデフォルトのコーデック レジストリを使用します。

1 つ以上の Codec クラスの動作をオーバーライドする必要があり、他のクラスのデフォルトのコーデック レジストリの動作を維持する場合は、優先順位に従ってすべてのレジストリを指定できます。たとえば、列挙型の Codec のデフォルトのプロバイダー動作をカスタム MyEnumCodec でオーバーライドする場合は、次の例に示すように、デフォルトのコーデック レジストリの前にそれをレジストリ リストに追加する必要があります。

val newRegistry = CodecRegistries.fromRegistries(
CodecRegistries.fromCodecs(MyEnumCodec()),
MongoClientSettings.getDefaultCodecRegistry()
)

このセクションのクラスとインターフェースの詳細については、次の API ドキュメントの各セクションを参照してください。

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 を使用する方法の例については、次のクラスのドライバー ソース コードを参照してください。

このセクションでは、 CodecCodecProviderを実装して、カスタム 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 {
@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
}
}

変換ロジックを定義した後、次の操作を実行できます。

  • Monolight のインスタンスのデータを MongoDB に保存する

  • MongoDB から次のインスタンスにデータを検索する Monolight

次のサンプル クラスには、MonolightCodecProviderwithCodecRegistry() メソッドに渡して 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 ドキュメントを参照してください。

戻る

Kotlin 直列化