Question

I defined BSONDocumentWriters to map domain objects (case classes) to BSON documents to be persisted in MongoDB using the ReactiveMongo driver. Defining the writers is quite straight-forward for case classes (although tedious and error prone: I wish there was a Salat-like solution for that). However, I can't seem to be able to do the same for a Map[String,Any] (where the values can be of type numeric, date or string). I found a code example that defines a generic writer (and reader) for a map:

implicit def MapWriter[V](implicit vw: BSONDocumentWriter[V]): BSONDocumentWriter[Map[String, V]] = 
  new BSONDocumentWriter[Map[String, V]] {
  def write(map: Map[String, V]): BSONDocument = {
    val elements = map.toStream.map { tuple =>
      tuple._1 -> vw.write(tuple._2)
    }
    BSONDocument(elements)
  }
}

but this does not work if there's no implicit BSONDocumentWriter for the type V, i.e., the snippet:

BSONDocument(
  "_id" -> "asd",
  "map" -> MapWriter[Any].write(Map("x" -> 1, "y" -> "2"))
)

does not compile:

could not find implicit value for parameter vw: reactivemongo.bson.BSONDocumentWriter[Any]
    "map" -> MapWriter[Any].write(Map("x" -> 1, "y" -> "2"))
                      ^

I thought maybe the writer should write to a BSONValue instead of a BSONDocument so I modified the example as follows:

implicit def ValueMapWriter[V](implicit vw: BSONWriter[V, BSONValue]): BSONDocumentWriter[Map[String, V]] = 
  new BSONDocumentWriter[Map[String, V]] {
  def write(map: Map[String, V]): BSONDocument = {
    val elements = map.toStream.map {
      tuple =>
        tuple._1 -> vw.write(tuple._2)
    }
    BSONDocument(elements)
  }
}

And to keep things simple, I tried to use Int as the value type but again, the snippet:

BSONDocument(
  "_id" -> "asd",
  "map" -> ValueMapWriter[Int].write(Map("x" -> 1, "y" -> 2))
)

does not compile:

could not find implicit value for parameter vw: reactivemongo.bson.BSONWriter[Int,reactivemongo.bson.BSONValue]
    "map" -> ValueMapWriter[Int].write(Map("x" -> 1, "y" -> 2))
                          ^

If the above had worked I could have used some base class as the value type and defined its implicit writer.

I am not sure why this is happening and how I can solve it. Maybe I am missing something obvious? Ideas?

Was it helpful?

Solution

The generic type parameter boundary for BSONValue in ValueMapWriter definition is not correct. If you change the line

implicit def ValueMapWriter[V](implicit vw: BSONWriter[V, BSONValue]): BSONDocumentWriter[Map[String, V]] =

with

implicit def ValueMapWriter[V](implicit vw: BSONWriter[V, _ <: BSONValue]): BSONDocumentWriter[Map[String, V]] =

then it should resolve the implicit writer for Int.

BTW simple-reactivemongo already does this. And I am also planning to add this functionality to ReactiveMongo Extensions.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top