Overview
このガイドでは、ScalaドライバーでScalaオブジェクトとBSONデータのエンコードとデコードを処理するコーデックとサポート クラスについて学習できます。The Codec Scala abstraction allows you to map any Scala type to a corresponding BSON type.これを使用して、Document や BsonDocument などの中間マップベースのオブジェクトに依存する代わりに、ドメイン オブジェクトをBSONとの間で直接マップします。
コーデック インターフェース
Codec インターフェースには、Scala オブジェクトを BSON データとの間でエンコードおよびデコードするための抽象メソッドが含まれています。これらのメソッドを実装して、BSON と、ユーザーの Codec 実装の Scala 型の間の変換ロジックを定義します。
Codecencode()インターフェースを実装するには、インターフェースの 、decode() 、getEncoderClass() メソッドを定義します。これらのメソッドを実装するコード例については、「 基本的なカスタム コーデックの例 」セクションを参照してください。
encode() メソッド
encode() メソッドは Scala 型のインスタンスを BSON にエンコードします。これにより、ドライバーはインスタンスを MongoDB に保存できます。このメソッドには次のパラメータが必要です。
Parameter Type | 説明 |
|---|---|
| BSON ドキュメントを書き込むためのメソッドを公開するインターフェースである |
| メソッドがエンコードするデータ。 |
| 現在の値を MongoDB コレクションに保存するかどうかなど、メソッドが BSON にエンコードする Scala オブジェクトに関するメタデータ。 |
encode() メソッドは値を返しません。
デコード() メソッド
decode() メソッドは、BSON データを使用して Scala 型のインスタンスをデコードします。このメソッドには次のパラメータが必要です。
Parameter Type | 説明 |
|---|---|
| BSON ドキュメントを読み取るためのメソッドを公開するインターフェースである |
| メソッドが Scala オブジェクトにデコードする BSON データに関するメタデータ。 |
getEncoderClass() メソッド
getEncoderClass() メソッドは、Codec で定義された Scala 型のインスタンスを返します。このメソッドは、Java 虚擬マシン (JVM) がランタイムに消去する型情報を提供します。
基本的なカスタム Codec の例
次のコード サンプルでは、PowerStatus シールドトレイトと PowerStatusCodec クラスを使用して、カスタム Codec を実装する方法を示します。
PowerStatus シールドトレイトは、電気スイッチの状態を表すのにケースオブジェクト On と Off を使用します。
sealed trait PowerStatus object PowerStatus { case object On extends PowerStatus case object Off extends PowerStatus }
PowerStatusCodec クラスは Codec を実装して、PowerStatus 値を対応する BSON ブール値にエンコードします。encode() メソッドは PowerStatus 値を BSON ブール値にエンコードし、decode() メソッドは BSON ブール値を PowerStatus 値にデコードします。
class PowerStatusCodec extends Codec[PowerStatus] { override def encode(writer: BsonWriter, value: PowerStatus, encoderContext: EncoderContext): Unit = { if (value != null) { writer.writeBoolean(value == PowerStatus.On) } } override def decode(reader: BsonReader, decoderContext: DecoderContext): PowerStatus = { if (reader.readBoolean()) PowerStatus.On else PowerStatus.Off } override def getEncoderClass: Class[PowerStatus] = classOf[PowerStatus] }
PowerStatusCodecクラスを使用するには、クラスのインスタンスをCodecRegistry Codecインターフェースに追加する必要があります。これにより、 は対応するScala型にマッピングされます。ドライバーは、CodecRegistry Codecにそのタイプの が含まれている場合にのみ、タイプをエンコードおよびデコードできます。カスタム をCodec CodecRegistryインターフェースに含める方法については、このページの「CodecRegistry」セクションを参照してください。
このセクションのクラスとインターフェースの詳細については、次の API ドキュメントを参照してください。
CodecRegistry コレクション
CodecRegistry は、Codec インスタンスの不可変コレクションです。CodecRegistry インスタンスを構築するには、次の CodecRegistries クラスの静的ファクトリメソッドのいずれかを使用します。各メソッドは、異なる Codec インスタンスのソースからレジストリをビルドします。
方式 | 説明 |
|---|---|
| メソッドに渡す |
| メソッドに渡す |
| メソッドに渡す他の |
次の例では、2 つの Codec 実装を使用しています。
IntegerCodec: BSON パッケージ内のCodecで、JavaInteger値を BSON 32ビット整数値にエンコードします。PowerStatusCodec:
CodecPowerStatus値をBSONブール値にデコードするサンプル 。
次の例は、fromCodecs() メソッドを使用してこれらの実装をレジストリに割り当てることで CodecRegistry インスタンスを構築する方法を示しています。
val codecRegistry = CodecRegistries.fromCodecs( new IntegerCodec(), new PowerStatusCodec() )
次の例では、前の例の Codec インスタンスを CodecRegistry コレクションから取得します。
val powerStatusCodec: Codec[PowerStatus] = codecRegistry.get(classOf[PowerStatus]) val integerCodec: Codec[Integer] = codecRegistry.get(classOf[Integer])
注意
未登録のクラスのCodecインスタンスでget()メソッドを呼び出すと、ドライバーはCodecConfigurationExceptionをスローします。
Default Codec Registry
デフォルトのコーデックCodecProvider レジストリは、一般的に使用されるScalaとMongoDB型間でエンコードする クラスのセットです。カスタム コーデック レジストリを指定しない限り、ドライバーは自動的にデフォルトのコーデック レジストリを使用します。CodecProvider インターフェースの詳細については、 CodecProvider セクションを参照してください。
1 つ以上の Codec クラスの動作をオーバーライドする必要があり、他のクラスのデフォルトのコーデック レジストリの動作を維持する場合は、優先順位に従ってすべてのレジストリを指定できます。たとえば、カスタム型の Codec のデフォルトのプロバイダー動作をカスタム MyEnumCodec でオーバーライドするには、デフォルトのコーデック レジストリの前にレジストリ リストに追加します。次の例はこのパターンを示しています。
val newRegistry = CodecRegistries.fromRegistries( CodecRegistries.fromCodecs(new MyEnumCodec()), MongoClientSettings.getDefaultCodecRegistry() )
このセクションのクラスとインターフェースの詳細については、次の API ドキュメントを参照してください。
CodecProvider インターフェース
CodecProvider インターフェースには、Codec インスタンスを作成し、それを CodecRegistry インスタンスに割り当てる抽象メソッドが含まれています。CodecRegistry インターフェースと同様に、CodecProvider インターフェースは Codec インスタンスを返す get() メソッドを定義します。BSON ライブラリはこれらの Codec インスタンスを使用して、Scala と BSON データ型間でエンコードします。
コードにクラスを追加する場合は、そのフィールドに対応する Codec オブジェクトが必要な場合は CodecProvider を使用します。各フィールドに固有の Codec インスタンスが必要な場合は、クラスの Codec インスタンスをインスタンス化する前に、各フィールドの Codec オブジェクトをインスタンス化する必要があります。get() メソッドの CodecRegistry パラメータを使用して、Codec が依存する Codec インスタンスのいずれかをコンストラクタに渡します。
次の例は、CodecProvider インターフェースを実装する方法を示しています。ドライバーは、実装された MonolightCodecProvider インターフェースを使用して、 Monolight クラスの MonolightCodec インスタンスを作成します。
class MonolightCodecProvider extends CodecProvider { override def get[T](clazz: Class[T], registry: CodecRegistry): Codec[T] = { if (clazz == classOf[Monolight]) { new MonolightCodec(registry).asInstanceOf[Codec[T]] } else { null } } }
CodecProviderカスタム クラスを含む インターフェースの完全な実装については、このガイドの「 完全なカスタム コーデックの例 」セクションを参照してください。
このセクションのクラスとインターフェースの詳細については、次の API ドキュメントを参照してください。
カスタム Codec の完全な例
このセクションでは、Codec および CodecProvider インターフェースを実装してカスタム Scala クラスのエンコードおよびデコード ロジックを定義する方法を学べることができます。このセクションでは、カスタム実装を指定して使用し、挿入操作と検索操作を実行する方法も示します。
次のコード スニペットは、例としてのカスタム クラス Monolight とそのフィールドを示しています。
case class Monolight( powerStatus: PowerStatus = PowerStatus.Off, colorTemperature: Int = 0 )
Monolight クラスには次のフィールドが含まれており、それぞれに Codec インターフェースの**実装**が必要です。
powerStatusライトがオンまたはオフに切り替えられたかどうかを示します。 PowerStatusCodecクラスはPowerStatus値をBSONブール値にエンコードします。colorTemperatureはライトの色を記述し、Int値が含まれます。BSON ライブラリに含まれるIntegerCodecクラスは、colorTemperature値を BSON 32ビット整数にエンコードします。
次のコード例は、Monolight クラスに Codec インターフェースを実装する方法を示しています。コンストラクターは CodecRegistry インスタンスを使用して、Monolight フィールドをエンコードおよびデコードするために必要な Codec インスタンスを検索します。
class MonolightCodec(registry: CodecRegistry) extends Codec[Monolight] { private val powerStatusCodec: Codec[PowerStatus] = registry.get(classOf[PowerStatus]) private val integerCodec: Codec[Integer] = registry.get(classOf[Integer]) override def encode(writer: BsonWriter, value: Monolight, encoderContext: EncoderContext): Unit = { writer.writeStartDocument() writer.writeName("powerStatus") powerStatusCodec.encode(writer, value.powerStatus, encoderContext) writer.writeName("colorTemperature") integerCodec.encode(writer, value.colorTemperature, encoderContext) writer.writeEndDocument() } override def decode(reader: BsonReader, decoderContext: DecoderContext): Monolight = { var powerStatus: PowerStatus = PowerStatus.Off var colorTemperature: Int = 0 reader.readStartDocument() while (reader.readBsonType() != BsonType.END_OF_DOCUMENT) { reader.readName() match { case "powerStatus" => powerStatus = powerStatusCodec.decode(reader, decoderContext) case "colorTemperature" => colorTemperature = integerCodec.decode(reader, decoderContext) case "_id" => reader.readObjectId() case _ => reader.skipValue() } } reader.readEndDocument() Monolight(powerStatus, colorTemperature) } override def getEncoderClass: Class[Monolight] = classOf[Monolight] }
以下のコード例は、Monolight クラスのフィールドの Codec インスタンスを構築し、カスタム CodecProvider を実装する方法を示しています。
class MonolightCodecProvider extends CodecProvider { override def get[T](clazz: Class[T], registry: CodecRegistry): Codec[T] = { if (clazz == classOf[Monolight]) { new MonolightCodec(registry).asInstanceOf[Codec[T]] } else { null } } }
get() メソッドは、ドライバーが Monolight クラスの Codec をリクエストすると、新しい MonolightCodec を返します。メソッドは CodecRegistry を MonolightCodec コンストラクタに渡し、MonolightCodec が PowerStatusCodec や IntegerCodec などのフィールドの Codec インスタンスを取得できるようにします。ドライバーが他のクラスの Codec をリクエストすると、メソッドは null を返します。
変換ロジックを定義した後、次の操作を実行できます。
Monolightクラスのインスタンスのデータを MongoDB に保存するMongoDBから
Monolightクラスのインスタンスにデータを取得する
次の例では、MonolightCodecProvider クラスを withCodecRegistry() メソッドに渡すことで、MongoCollection インスタンスに割り当てています。次に、insertOne() メソッドを呼び出して新しい Monolight インスタンスをコレクションに挿入し、find() メソッドを呼び出して保存された Monolight インスタンスを返します。出力には、取得された Monolight インスタンスが表示され、カスタム コーデックがデータを正常にエンコードおよびデコードしたことが確認されます。
object MonolightCodecExample { def main(args: Array[String]): Unit = { val uri = "<connection string URI>" val mongoClient = MongoClient(uri) val codecRegistry = CodecRegistries.fromRegistries( CodecRegistries.fromCodecs( new IntegerCodec(), new PowerStatusCodec() ), CodecRegistries.fromProviders(new MonolightCodecProvider()), MongoClientSettings.getDefaultCodecRegistry() ) val database = mongoClient.getDatabase("codecs_example_products") val collection: MongoCollection[Monolight] = database .getCollection[Monolight]("monolights") .withCodecRegistry(codecRegistry) val myMonolight = Monolight(PowerStatus.On, 5200) Await.result( collection.insertOne(myMonolight).toFuture(), Duration.Inf ) val lights = Await.result( collection.find().toFuture(), Duration.Inf ) println(lights) mongoClient.close() } }
このセクションで説明されるメソッドとクラスの詳細については、次の API ドキュメントを参照してください。