pbson is a BSON scala library.

The goal of this library is to create at compile-time the boilerplate necessary to encode and decode of a certain type. The pbson provides generic codec derivation using Shapeless. This library provides another way encode and decode case classes for mongo scala driver. Decoder instead of throw Exception return Either[BsonError, T]

pbson can derive bson encoder and decoder:

            
BsonEncoder[T] : T => BsonValue
BsonDecoder[T] : BsonValue => Either[BsonError, T]
        

Quick Start

If you're using SBT, add the following line to your build file:


libraryDependencies += "ru.twistedlogic" %% "pbson" % "0.0.20"
            
Link to code
                
import pbson._
import pbson.semiauto._

case class MyId(value: String) extends AnyVal

case class TestCase(a: Int, b: Option[String], id: MyId)

implicit val testCaseEncoder: BsonEncoder[TestCase] = deriveEncoder
implicit val testCaseDecoder: BsonDecoder[TestCase] = deriveDecoder

val test = TestCase(3, Some("45"), MyId("000"))

val bson = test.toBson
println(bson)
// { "a" : 3, "b" : "45", "id" : "000" }
println(bson.fromBson[TestCase]())
// Right(TestCase(3,Some(45),MyId(000)))
            

AnyVal wrappers

AnyVal wrappers encode and decode as value.

Link to code

case class MyId(value: String) extends AnyVal

implicit val testCaseEncoder: BsonEncoder[MyId] = deriveEncoder
implicit val testCaseDecoder: BsonDecoder[MyId] = deriveDecoder

val test = MyId("000")

val bson = test.toBson
println(bson)
// BsonString{value='000'}
println(bson.fromBson[MyId]())
// Right(MyId(000))
            

ADT (Algebraic Data Types)

semiauto

Link to code

sealed trait ADT

object ADT {
  case class A(s: String) extends ADT
  case class B(a: Int) extends ADT
}

import ADT._

implicit val adtEncoder: BsonEncoder[ADT] = deriveEncoder
implicit val adtDecoder: BsonDecoder[ADT] = deriveDecoder

val test : ADT = B(4)

val bson = test.toBson
println(bson)
// { "a" : 4, "type" : "B" }
println(bson.fromBson[ADT])
// Right(B(4))
            

manual enumeration

Link to code

sealed trait ADT

object ADT {
  final case object A extends ADT
  final case object B extends ADT
}

import ADT._

implicit val adtEnumEncoder: BsonEncoder[ADT] = asStringEncoder {
  case A => "A"
  case B => "B"
}

implicit val adtEnumDecoder: BsonDecoder[ADT] = asStringDecoder {
  case "A" => A
  case "B" => B
}

val test : ADT = B

val bson = test.toBson
println(bson)
// BsonString{value='B'}
println(bson.fromBson[ADT])
// Right(B)
            

Map

as object

Link to code

case class TestCase(a: Map[String, String])

implicit val testCaseEncoder: BsonEncoder[TestCase] = deriveEncoder
implicit val testCaseDecoder: BsonDecoder[TestCase] = deriveDecoder

val test : TestCase = TestCase(Map("key1" -> "value1", "key2" -> "value2"))

val bson = test.toBson
println(bson)
// { "a" : { "key1" : "value1", "key2" : "value2" } }
println(bson.fromBson[TestCase])
// Right(TestCase(Map(key1 -> value1, key2 -> value2)))
                        

map as array

Link to code

case class TestCase(a: Map[String, String])

implicit val testCaseEncoder: BsonEncoder[TestCase] = deriveEncoder
implicit val testCaseDecoder: BsonDecoder[TestCase] = deriveDecoder

implicit val mapEncoder: ReprBsonMaybeEncoder[Map[String, String]] = map2ArrayEncoder
implicit val mapDecoder: BsonDecoder[Map[String, String]] = array2MapDecoder

val test : TestCase = TestCase(Map("key1" -> "value1", "key2" -> "value2"))

val bson = test.toBson
println(bson)
// { "a" : [{ "_v" : "value1", "_k" : "key1" }, { "_v" : "value2", "_k" : "key2" }] }
println(bson.fromBson[TestCase])
// Right(TestCase(Map(key1 -> value1, key2 -> value2)))
                        

map (key, value case classes) as array

Link to code

case class Key(k: String)
case class Value(v: String)
case class TestCase(a: Map[Key, Value])

implicit val mapEncoder: ReprBsonMaybeEncoder[Map[Key, Value]] = map2ArrayEncoder
implicit val mapDecoder: BsonDecoder[Map[Key, Value]] = array2MapDecoder

implicit val testCaseEncoder: BsonEncoder[TestCase] = deriveEncoder
implicit val testCaseDecoder: BsonDecoder[TestCase] = deriveDecoder

val test : TestCase = TestCase(Map(Key("45") -> Value("34"), Key("23") -> Value("45")))

val bson = test.toBson
println(bson)
// { "a" : [{ "v" : "34", "k" : "45" }, { "v" : "45", "k" : "23" }] }
println(bson.fromBson[TestCase])
// Right(TestCase(Map(Key(45) -> Value(34), Key(23) -> Value(45))))
                        

Enumeration

Link to code

object WeekDay extends Enumeration {
type WeekDay = Value
  val Mon, Tue, Wed, Thu, Fri, Sat, Sun = Value
}

implicit val weekDayEncoder: BsonEncoder[WeekDay.Value] = enumEncoder(WeekDay)
implicit val weekDayDecoder: BsonDecoder[WeekDay.Value] = enumDecoder(WeekDay)

val test : WeekDay.Value = WeekDay.Thu

val bson = test.toBson
println(bson)
// BsonString{value='Thu'}
println(bson.fromBson[WeekDay.Value])
// Right(Thu)
                        

Cursor

Link to code

val doc = BsonDocument(
  "a" -> BsonDocument(
    "a" -> BsonInt64(4l),
    "b" -> BsonArray(BsonString("45"), BsonString("23"))
  ),
  "b" -> BsonArray(BsonString("45"), BsonString("23"))
)

val av = doc.cursor().down("a").get[Long]("a")
println(av)
// Right(4)