Frage

Hier ist ein einfacher JSON, den ich in/aus MongoDB schreiben/lesen möchte:

{
  "id": "ff59ab34cc59ff59ab34cc59",
  "name": "Joe",
  "surname": "Cocker"
}

Bevor Sie es in MongoDB speichern, "ff59ab34cc59ff59ab34cc59" muss in eine umgewandelt werden ObjectID Und id umbenannt in _id...also folgendes gegeben Reads, wie erreiche ich das?

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

Und natürlich brauche ich auch das Gegenteil für mich Writes, d.h.Umbenennung _id Zu id und Umwandeln eines ObjectID zu reinem Text im Format "ff59ab34cc59ff59ab34cc59".

War es hilfreich?

Lösung

JsonExtensions

Normalerweise habe ich in meiner Anwendung ein JsExtensions-Objekt, das wie folgt aussieht:

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
}

Für ein einfaches Modell

case class SOUser(name:String,_id:BSONObjectID)

Sie können Ihren JSON-Serializer/Deserializer wie folgt schreiben:

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)
  }
}

Folgendes erhalten Sie in der Konsole:

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)

Wenden Sie dies auf Ihr Beispiel an

val _personReads: Reads[JsObject] = (
  (__ \ 'id).read[String] ~
  (__ \ 'name).read[String] ~
  (__ \ 'surname).read[String]
).reduce

Wird standardmäßig nicht kompiliert. Ich denke, Sie wollten schreiben:

val _personReads: Reads[(String,String,String)] = (
  (__ \ 'id).read[String] ~
  (__ \ 'name).read[String] ~
  (__ \ 'surname).read[String]
).tupled

In diesem Fall können Sie Folgendes tun

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)

oder Sie meinten, dass Sie den Wert des ID-Schlüssels verschieben möchten _id \ $oid was mit bewerkstelligt werden kann

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)

Sie können dort derzeit keine BSONObjectID haben, da Sie Objekte aus der JsValue-Typhierarchie bearbeiten.Wenn Sie JSON an reactivemongo übergeben, wird es in einen BSONValue konvertiert.Ein JsObject wird in ein BSONDocument konvertiert.wenn das JsObject einen Pfad für enthält _id\$oid Dieser Pfad wird automatisch in eine BSONObjectId konvertiert und als ObjectID in mongodb gespeichert.

Andere Tipps

Bei der ursprünglichen Frage geht es eigentlich um reactivemongos (Sgodbillon et al) Behandlung des einheimischen Mongodb _id.Die gewählte Antwort ist aufschlussreich und richtig, geht jedoch indirekt auf die Bedenken des OP ein, ob „alles einfach funktionieren wird“.

Dank an https://github.com/ReactiveMongo/ReactiveMongo-Play-Json/blob/e67e507ecf2be48cc71e429919f7642ea421642c/src/main/scala/package.scala#L241-L255, ich glaube, das wird es.

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)

Das Obige ist ein minimales Arbeitsbeispiel für die Verwendung Ihrer Fallklasse id und die Datenbank speichert es als _id.Beide werden als 12-Byte instanziiert BSONObjectIDS.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top