使用案例 class 进行 json 验证和 MongoDB 持久性(Reactivemongo),id 呢?
Using case class for json validation and MongoDB persistense (Reactivemongo), what about id?
所以,我有一个案例 class 以及 JSON 和 BSONDocument 格式的读者和作者。
问题是,当插入到 MongoDB 时,我希望能够指定 BSONObjectID,因此我可以在创建时 return 它。但是,如果我在 class 的情况下添加 id: BSONObjectID
,我找不到让 JSON validation/transformation 工作的方法。
这是我的代码:
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))
我真的很想避免使用同一模型的重复模型表示。有什么想法吗?
使用 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")
}
}
如果您不需要模型本身的 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")
}
}
在更新时,id 由 POST 请求提供。
当您查询数据库时,您可以使用以下基本实现来获取临时结果集的 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])))
}
所以,我有一个案例 class 以及 JSON 和 BSONDocument 格式的读者和作者。
问题是,当插入到 MongoDB 时,我希望能够指定 BSONObjectID,因此我可以在创建时 return 它。但是,如果我在 class 的情况下添加 id: BSONObjectID
,我找不到让 JSON validation/transformation 工作的方法。
这是我的代码:
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))
我真的很想避免使用同一模型的重复模型表示。有什么想法吗?
使用 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")
}
}
如果您不需要模型本身的 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")
}
}
在更新时,id 由 POST 请求提供。
当您查询数据库时,您可以使用以下基本实现来获取临时结果集的 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])))
}