使用case类进行json验证和MongoDB persistense(Reactivemongo),id怎么样?

时间:2015-01-15 22:44:26

标签: mongodb scala playframework-2.0 reactivemongo play-reactivemongo

所以,我有一个案例类以及JSON和BSONDocument格式的读者和作者。

问题是,当插入MongoDB时,我希望能够指定BSONObjectID,所以我可以在创建时返回它。但是,如果我在案例类中添加id: BSONObjectID,我找不到让JSON验证/转换工作的方法。

这是我的代码:

case class Mini(username: String, email: String, quizAnswer1: List[String] )

implicit object MiniWriter extends BSONDocumentWriter[Mini] {
  def write(mini: Mini): BSONDocument = BSONDocument(
    "username" -> mini.username,
    "email" -> mini.email,
    "quizAnswer1" -> mini.quizAnswer1
  )
}

implicit object MiniReader extends BSONDocumentReader[Mini] {
  def read(doc: BSONDocument): Mini = Mini(
    doc.getAs[String]("username").get,
    doc.getAs[String]("email").get,
    doc.getAs[List[String]]("quizAnswer1").toList.flatten
  )
}

implicit val miniReads: Reads[Mini] = (
  (JsPath \ "username").read[String] and
  (JsPath \ "email").read[String] and
  (JsPath \ "quizAnswer1").read[List[String]]
)(Mini.apply _)

implicit val miniWrites: Writes[Mini] = (
  (JsPath \ "username").write[String] and
  (JsPath \ "email").write[String] and
  (JsPath \ "quizAnswer1").write[List[String]]
)(unlift(Mini.unapply))

我真的想避免使用同一模型的重复模型表示。有什么想法吗?

2 个答案:

答案 0 :(得分:1)

如果您不需要模型中的id,您可以在操作时临时使用它。在PUT上,您可以使用以下基本实现:

def insert(t: T)(implicit ctx: ExecutionContext): Future[BSONObjectID] = {
    val id = BSONObjectID.generate
    val obj = format.writes(t).as[JsObject]
    obj \ "_id" match {
      case _: JsUndefined =>
        coll.insert(obj ++ Json.obj("_id" -> id)).map { _ => id }

     case JsObject(Seq((_, JsString(oid)))) =>
        coll.insert(obj).map { _ => BSONObjectID(oid) }

     case JsString(oid) =>
        coll.insert(obj).map { _ => BSONObjectID(oid) }

     case f => sys.error(s"Could not parse _id field: $f")
}

}

在更新时,POST请求提供了id。

当您查询数据库时,您可以使用以下基本实现来获取结果集临时的ID

def find(sel: JsObject, limit: Int = 0, skip: Int = 0, sort: JsObject = Json.obj(), projection: JsObject = Json.obj())(implicit ctx: ExecutionContext): Future[Traversable[(T, BSONObjectID)]] = {
    val cursor = coll.find(sel).projection(projection).sort(sort).options(QueryOpts().skip(skip).batchSize(limit)).cursor[JsObject]
    val l = if (limit != 0) cursor.collect[Traversable](limit) else cursor.collect[Traversable]()
    l.map(_.map(js => (js.as[T], (js \ "_id").as[BSONObjectID])))
}

答案 1 :(得分:0)

使用play你可以直接使用play的json fomrat / read / write:

https://www.playframework.com/documentation/2.3.x/ScalaJsonCombinators

示例如下:

object Mini {
    implicit val miniFormat: Format[Mini] = Json.format[Mini]
}

据我所知,您必须使用'_id'声明id对象。

case class Mini(_id:BSONObjectId, username: String, email: String, quizAnswer1: List[String] )

对于BSONObjectId的隐式转换,您可以使用以下格式:

implicit object BSONObjectIDFormat extends Format[BSONObjectID] {
def writes(objectId: BSONObjectID): JsValue = {
  Json.obj("$oid" -> JsString(objectId.stringify))
}
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 JsObject(Seq((_, oid))) =>
    reads(oid)

  case _ => JsError("Expected BSONObjectID as JsString")
}

}