这是一个我想写/读MongoDB的简单JSON:
{
"id": "ff59ab34cc59ff59ab34cc59",
"name": "Joe",
"surname": "Cocker"
}
在将其存储到MongoDB之前,必须将"ff59ab34cc59ff59ab34cc59"
转换为ObjectID
并将id
重命名为_id
...以便给出以下Reads
,我该如何实现?
val personReads: Reads[JsObject] = (
(__ \ 'id).read[String] ~ // how do I rename id to _id AND transform "ff59ab34cc59ff59ab34cc59" to an ObjectID?
(__ \ 'name).read[String] ~
(__ \ 'surname).read[String]
) reduce
当然,对于Writes
,我也需要相反,即将_id
重命名为id
并将ObjectID
转换为{{1}格式的纯文本}}
答案 0 :(得分:11)
<强> JsonExtensions 强>
我的应用程序中通常有一个JsExtensions对象,如下所示:
import reactivemongo.bson.BSONObjectID
object JsonExtensions {
import play.api.libs.json._
def withDefault[A](key: String, default: A)(implicit writes: Writes[A]) = __.json.update((__ \ key).json.copyFrom((__ \ key).json.pick orElse Reads.pure(Json.toJson(default))))
def copyKey(fromPath: JsPath,toPath:JsPath ) = __.json.update(toPath.json.copyFrom(fromPath.json.pick))
def copyOptKey(fromPath: JsPath,toPath:JsPath ) = __.json.update(toPath.json.copyFrom(fromPath.json.pick orElse Reads.pure(JsNull)))
def moveKey(fromPath:JsPath, toPath:JsPath) =(json:JsValue)=> json.transform(copyKey(fromPath,toPath) andThen fromPath.json.prune).get
}
对于简单模型
case class SOUser(name:String,_id:BSONObjectID)
你可以这样写你的json序列化器/反序列化器:
object SOUser{
import play.api.libs.json.Format
import play.api.libs.json.Json
import play.modules.reactivemongo.json.BSONFormats._
implicit val soUserFormat= new Format[SOUser]{
import play.api.libs.json.{JsPath, JsResult, JsValue}
import JsonExtensions._
val base = Json.format[SOUser]
private val publicIdPath: JsPath = JsPath \ 'id
private val privateIdPath: JsPath = JsPath \ '_id \ '$oid
def reads(json: JsValue): JsResult[SOUser] = base.compose(copyKey(publicIdPath, privateIdPath)).reads(json)
def writes(o: SOUser): JsValue = base.transform(moveKey(privateIdPath,publicIdPath)).writes(o)
}
}
这是您在控制台中获得的内容:
scala> import reactivemongo.bson.BSONObjectID
import reactivemongo.bson.BSONObjectID
scala> import models.SOUser
import models.SOUser
scala> import play.api.libs.json.Json
import play.api.libs.json.Json
scala>
scala> val user = SOUser("John Smith", BSONObjectID.generate)
user: models.SOUser = SOUser(John Smith,BSONObjectID("52d00fd5c912c061007a28d1"))
scala> val jsonUser=Json.toJson(user)
jsonUser: play.api.libs.json.JsValue = {"name":"John Smith","id":"52d00fd5c912c061007a28d1","_id":{}}
scala> Json.prettyPrint(jsonUser)
res0: String =
{
"name" : "John Smith",
"id" : "52d00fd5c912c061007a28d1",
"_id" : { }
}
scala> jsonUser.validate[SOUser]
res1: play.api.libs.json.JsResult[models.SOUser] = JsSuccess(SOUser(John Smith,BSONObjectID("52d00fd5c912c061007a28d1")),/id)
将此应用于您的示例
val _personReads: Reads[JsObject] = (
(__ \ 'id).read[String] ~
(__ \ 'name).read[String] ~
(__ \ 'surname).read[String]
).reduce
默认情况下不编译,我想你打算写:
val _personReads: Reads[(String,String,String)] = (
(__ \ 'id).read[String] ~
(__ \ 'name).read[String] ~
(__ \ 'surname).read[String]
).tupled
在这种情况下,您可以执行以下操作
import play.api.libs.json._
import play.api.libs.json.Reads._
import play.api.libs.functional.syntax._
import play.modules.reactivemongo.json.BSONFormats._
import reactivemongo.bson.BSONObjectID
def copyKey(fromPath: JsPath,toPath:JsPath ) = __.json.update(toPath.json.copyFrom(fromPath.json.pick))
val json = """{
"id": "ff59ab34cc59ff59ab34cc59",
"name": "Joe",
"surname": "Cocker"
}"""
val originaljson = Json.parse(json)
val publicIdPath: JsPath = JsPath \ 'id
val privateIdPath: JsPath = JsPath \ '_id \ '$oid
val _personReads: Reads[(BSONObjectID,String,String)] = (
(__ \ '_id).read[BSONObjectID] ~
(__ \ 'name).read[String] ~
(__ \ 'surname).read[String]
).tupled
val personReads=_personReads.compose(copyKey(publicIdPath,privateIdPath))
originaljson.validate(personReads)
// yields res5: play.api.libs.json.JsResult[(reactivemongo.bson.BSONObjectID, String, String)] = JsSuccess((BSONObjectID("ff59ab34cc59ff59ab34cc59"),Joe,Cocker),/id)
或者你的意思是你要将id键的值移动到_id \ $oid
,这可以用
import play.api.libs.json._
import play.api.libs.json.Reads._
import play.api.libs.functional.syntax._
import play.modules.reactivemongo.json.BSONFormats._
import reactivemongo.bson.BSONObjectID
def copyKey(fromPath: JsPath,toPath:JsPath ) = __.json.update(toPath.json.copyFrom(fromPath.json.pick))
val json = """{
"id": "ff59ab34cc59ff59ab34cc59",
"name": "Joe",
"surname": "Cocker"
}"""
val originaljson = Json.parse(json)
val publicIdPath: JsPath = JsPath \ 'id
val privateIdPath: JsPath = JsPath \ '_id \ '$oid
originaljson.transform(copyKey(publicIdPath,privateIdPath) andThen publicIdPath.json.prune)
由于您正在从JsValue类型层次结构中操作对象,因此您现在不能拥有BSONObjectID。当您将json传递给reactivemongo时,它将转换为BSONValue。 JsObject将转换为BSONDocument。如果JsObject包含_id\$oid
的路径,则此路径将自动转换为BSONObjectId,并将其作为ObjectID存储在mongodb中。
答案 1 :(得分:0)
最初的问题实际上是关于本土mongodb _id
的反应性( sgodbillon等)治疗。所选择的答案具有指导性和正确性,但却倾向于解决OP关注的问题,即它是否能够正常运转&#34;。
import scala.concurrent.Await
import scala.concurrent.duration.Duration
import play.api.libs.concurrent.Execution.Implicits.defaultContext
import play.api.libs.functional.syntax._
import play.api.libs.json._
import play.modules.reactivemongo.json.collection.JSONCollection
import reactivemongo.api._
import reactivemongo.bson.BSONObjectID
import reactivemongo.play.json._
case class Person(
id: BSONObjectID,
name: String,
surname: String
)
implicit val PersonFormat: OFormat[Person] = (
(__ \ "_id").format[BSONObjectID] and
(__ \ "name").format[String] and
(__ \ "surname").format[String]
)(Person.apply, unlift(Person.unapply))
val driver = new reactivemongo.api.MongoDriver
val connection = driver.connection(List("localhost"))
val db = connection.db("test")
val coll = db.collection[JSONCollection]("persons")
coll.drop(false)
val id = BSONObjectID.generate()
Await.ready(coll.insert(Person(id, "Joe", "Cocker")), Duration.Inf)
Await.ready(coll.find(Json.obj()).one[Person] map { op => assert(op.get.id == id, {}) }, Duration.Inf)
以上是使用id
的案例类的最小工作示例,以及将其存储为_id
的数据库。两者都被实例化为12字节BSONObjectID
s。