Macros
On this page
Overview
The Scala driver allows you to use case classes to represent
documents in a collection by using the Macros
helper. Simple case classes and
nested case classes are supported. Hierarchical modeling can be achieved
by using a sealed trait or class and then by having case classes implement the
parent trait.
Many simple Scala types are supported and can be marshaled into their
corresponding BsonValue
type. The following list describes Scala types and their
type-safe BSON representation:
Scala Type | BSON Type |
---|---|
Case class | Document |
Iterable | Array |
Date | Date |
Boolean | Boolean |
Double | Double |
Int | Int32 |
Long | Int64 |
String | String |
Array[Byte] | Binary |
None | Null |
Creating Codecs
To create a codec for your case class, use the Macros
object helper
methods. You should use the
Macros.createCodecProvider()
method to create a CodecProvider
. A
CodecProvider
passes the configured CodecRegistry
to the underlying
Codec
and provides access to all the configured codecs.
To create a CodecProvider
, set the case class type when calling
createCodecProvider()
as shown in the following code:
import org.mongodb.scala.bson.codecs.Macros case class Person(firstName: String, secondName: String) val personCodecProvider = Macros.createCodecProvider[Person]()
The personCodecProvider
can then be used when converted into a
CodecRegistry
by using the CodecRegistries
static helpers. The
following code creates a new codec registry, combining the new personCodecProvider
and the default codec registry:
import org.mongodb.scala.bson.codecs.DEFAULT_CODEC_REGISTRY import org.bson.codecs.configuration.CodecRegistries.{fromRegistries, fromProviders} val codecRegistry = fromRegistries( fromProviders(personCodecProvider), DEFAULT_CODEC_REGISTRY )
The Macros
helper also has an implicit createCodecProvider()
method that
takes the Class[T]
and creates a CodecProvider
from that. This method
is more concise especially when defining multiple providers:
import org.mongodb.scala.bson.codecs.Macros._ import org.mongodb.scala.bson.codecs.DEFAULT_CODEC_REGISTRY import org.bson.codecs.configuration.CodecRegistries.{fromRegistries, fromProviders} case class Address(firstLine: String, secondLine: String, thirdLine: String, town: String, zipCode: String) case class ClubMember(person: Person, address: Address, paid: Boolean) val codecRegistry = fromRegistries( fromProviders(classOf[ClubMember], classOf[Person], classOf[Address]), DEFAULT_CODEC_REGISTRY )
Sealed Classes and ADTs
Hierarchical class structures are supported through sealed traits and
classes. Each subclass is handled specifically by the generated codec,
so you only need to create a CodecProvider
for the parent sealed
trait or class. Internally an extra field (_t
) is stored alongside the data
so that the correct subclass can be hydrated when decoding the data.
The following code is an example of a tree-like structure containing branch and leaf
nodes:
sealed class Tree case class Branch(b1: Tree, b2: Tree, value: Int) extends Tree case class Leaf(value: Int) extends Tree val codecRegistry = fromRegistries( fromProviders(classOf[Tree]), DEFAULT_CODEC_REGISTRY )
Options and None Values
By default, Option
values are always stored. In driver v2.1.0, new
helpers were added so that None
values would not be stored in the
database. In the following example, the driver only stores an address if
if one is present:
import org.mongodb.scala.bson.codecs.Macros case class Person(firstName: String, secondName: String, address: Option[Address]) val personCodecProvider = Macros.createCodecProviderIgnoreNone[Person]()
Alternative Field Names
The BsonProperty
annotation can be used to configure the BSON field
key to be used for a given property. The following example uses the
BsonProperty
annotation to change how the firstName
field is stored:
case class Person(@BsonProperty("first_name") firstName: String, secondName: String)