Reputation: 59
Using the ReactiveMongo driver I am trying to serialize a Map of potentially unknown keys/fields of
mixed types Map[String, Any]
into a BSONDocument. My question is quite similar to that asked here which refers to the ReactiveMongo documentation example of serializing maps here. Unfortunately the answer only seems to work for Maps of a consistent type whereas I need it for Any. My MapWriter is 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)
}
}
The following compiles and serializes just fine as all types are Ints
val allInts = Map("someInt" -> 1, "someOtherInt" -> 2)
val docFromMap: BSONDocument = BSON.writeDocument(allInts)
However I cannot seem to massage it into something that will handle Map[String,Any]
val mixedTypes = Map("someDouble" -> 234.324, "someString" -> "abc", "someInt" -> 1)
val docFromMap: BSONDocument = BSON.writeDocument(mixedTypes)
results in:
Error:(134, 54) could not find implicit value for parameter writer: reactivemongo.bson.BSONWriter[scala.collection.immutable.Map[String,Any],reactivemongo.bson.BSONDocument]
val docFromMap: BSONDocument = BSON.writeDocument(mixedTypes)
^
There is likely some fundamental concept of implicits that I'm missing in order to accomplish this, without explicitly knowing the types of each field in the map at compile time.
Upvotes: 1
Views: 1015
Reputation: 59
Taking the advice from cchantep, I went down the road of making things more explicitly typesafe by matching the different types in the map and writing them to the document. This can certainly be expanded to handle nested maps and arrays. A good example of this can be seen here: JsBSONHandler Gist. For my scenario listed above with a flat map of mixed types, this works just fine for the value types that I care about:
def writeObject(obj: Map[String,Any]): BSONDocument = BSONDocument(obj.map(writePair))
def writePair(p: (String, Any)): (String, BSONValue) = (p._1, p._2 match {
case value: String => BSONString(value)
case value: Double => BSONDouble(value)
case value: Int => BSONInteger(value)
case value: Boolean => BSONBoolean(value)
case value: Long => BSONLong(value)
case other => BSONNull
})
Usage:
val mixedTypes = Map("someDouble" -> 234.324, "someString" -> "abc", "someInt" -> 1)
val docFromMap: BSONDocument = writeObject(mixedTypes)
Upvotes: 2