Docs Menu
Docs Home
/ / /
Scala
/

Macros

On this page

  • Overview
  • Creating Codecs
  • Sealed Classes and ADTs
  • Options and None Values
  • Alternative Field Names

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

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 )

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 )

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]()

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)

Back

Documents