1

i am new in Scala and Play, i am trying to map Scala BSONObjectID to mongo ObjectId. I got many samples from internet, but still stuck in one compile time error. Following is code of my Case Class:

case class UserDetail(
 val _id: Option[BSONObjectID],
 val name: String,
 val age: Double,
 var created: Option[Long]
)  

object UserDetail{
 implicit val userDetailReads: Reads[UserDetail] = (
 (JsPath \ "_id").readNullable[BSONObjectID] and
 (JsPath \ "name").read[String] and
 (JsPath \ "age").read[Double] and
 (JsPath \ "created").readNullable[Long]
)(UserDetail.apply _)

implicit val userDetailWrites: Writes[UserDetail] = (
(JsPath \ "_id").writeNullable[BSONObjectID]and
(JsPath \ "name").write[String] and
(JsPath \ "age").write[Double] and
(JsPath \ "created").writeNullable[Long]
)(unlift { UserDetail.unapply })}

This (JsPath \ "_id").readNullable[BSONObjectID] generate compile time error as below:

not enough arguments for method readNullable: (implicit r: play.api.libs.json.Reads[reactivemongo.bson.BSONObjectID])play.api.libs.json.Reads[Option[reactivemongo.bson.BSONObjectID]]. Unspecified value parameter r.
not enough arguments for method readNullable: (implicit r: play.api.libs.json.Reads[reactivemongo.bson.BSONObjectID])play.api.libs.json.Reads[Option[reactivemongo.bson.BSONObjectID]]. Unspecified value parameter r.

This (JsPath \ "_id").writeNullable[BSONObjectID] also generate same error.

I want to format my json request. So i am using custom formatter as below:

object BSONObjectIDFormat extends Format[BSONObjectID]{

 def writes(objectId: BSONObjectID): JsValue = JsString(objectId.toString())

 def reads(json: JsValue): JsResult[BSONObjectID] = json match {
  case JsString(x) => {
  val maybeOID: Try[BSONObjectID] = BSONObjectID.parse(x)
  if(maybeOID.isSuccess) JsSuccess(maybeOID.get) else {
    JsError("Expected BSONObjectID as JsString")
  }
}
case _ => JsError("Expected BSONObjectID as JsString")
}}

My Json request as below:

{
"_id":{"$oid":"54fd4b7084071e6a6ab13cee"},
"name" : "Akka",
"age" : 30,
"created" : 1425886070013
}

When i send the JSON request, i got following error:

[error] D:\play_projects\scala_play_sample\app\models\UserDetail.scala:35: No Js
on deserializer found for type reactivemongo.bson.BSONObjectID. Try to implement
an implicit Reads or Format for this type.
[error]     (JsPath \ "_id").readNullable[BSONObjectID] and
[error]                                  ^
[error] D:\play_projects\scala_play_sample\app\models\UserDetail.scala:42: No Js
on serializer found for type reactivemongo.bson.BSONObjectID. Try to implement a
n implicit Writes or Format for this type.
[error]     (JsPath \ "_id").writeNullable[BSONObjectID]and
cchantep
  • 9,118
  • 3
  • 30
  • 41
Harmeet Singh Taara
  • 6,483
  • 20
  • 73
  • 126

2 Answers2

0

In this case, we just initialize our custom formatter with in the Case class. I am not sure, why the play not automatically pic the fomatter. Now the code as below:

case class UserDetail(
 val _id: Option[BSONObjectID],
 val name: String,
 val age: Double,
 var created: Option[Long]
)  

object UserDetail{

 implicit val idFormatter = BSONObjectIDFormat

 implicit val userDetailReads: Reads[UserDetail] = (
 (JsPath \ "_id").readNullable[BSONObjectID] and
 (JsPath \ "name").read[String] and
 (JsPath \ "age").read[Double] and
 (JsPath \ "created").readNullable[Long]
)(UserDetail.apply _)

 implicit val userDetailWrites: Writes[UserDetail] = (
 (JsPath \ "_id").writeNullable[BSONObjectID]and
 (JsPath \ "name").write[String] and
 (JsPath \ "age").write[Double] and
 (JsPath \ "created").writeNullable[Long]
 )(unlift { UserDetail.unapply })}
Harmeet Singh Taara
  • 6,483
  • 20
  • 73
  • 126
  • 1
    Having a BSON specific type in a case class of the model is not generally recommended. It ties the definition of the app model with details implementation of underlying persistence library. – cchantep Mar 17 '15 at 12:49
  • Hello @cchantep instead of BSON specific type. what we need to use? Because when i use String type, I am unable to do queries like find by id etc. – Harmeet Singh Taara Mar 17 '15 at 13:42
0

For reactivemongo 1.0.7, it's pretty easy to mapping ObjectId to case class.

Reads and Macros are sufficient for most situations.

import reactivemongo.api.bson.Macros.Annotations.{Key, Reader}
import reactivemongo.api.bson.{BSONDocumentHandler, BSONObjectID, BSONReader, Macros}

object Model {
  val idReader: BSONReader[String] = BSONReader.collect[String] { case id @ BSONObjectID(_) =>
    id.asInstanceOf[BSONObjectID].stringify
  }

  case class Post(@Reader(idReader) @Key("_id") id: String, title: String, content: String, created: Long)

  implicit val postHandler: BSONDocumentHandler[Post] = Macros.handler[Post]
}
gcnyin
  • 1