2016-05-04 8 views
0

Ich verwende ReactiveMongo 0.11.11 für Play 2.5 und möchte ein BSONDocument in ein JsObject konvertieren.BSONDokument zu JsObject und überschreiben BSONDateTimeFormat

Für die meisten BSON-Datentypen (String, Int ...) sind die Standardwerte vollkommen in Ordnung, damit die Bibliothek die Aufgabe ausführen kann. Für BSON Typ DateTime (BSONDateTime) gibt mir der Wert der JSON-Eigenschaft nicht das Format, das ich brauche.

Der JSON-Wert für ein Datum ist ein JSObject mit Eigenschaftsnamen $date und ein UNIX-Zeitstempel in Millisekunden als Wert:

{ 
    "something": { 
     "$date": 1462288846873 
    } 
} 

Die JSON ich so eine String-Darstellung des Datums ist will:

Leider weiß ich nicht, wie Sie das Standardverhalten überschreiben, ohne alles neu zu schreiben oder Code in der Bibliothek selbst zu ändern. Diese

ist, wo ich denke, es passiert (source code):

val partialWrites: PartialFunction[BSONValue, JsValue] = { 
    case dt: BSONDateTime => Json.obj("$date" -> dt.value) 
} 

Meine Version würde wie folgt aussehen haben:

val partialWrites: PartialFunction[BSONValue, JsValue] = { 
    case dt: BSONDateTime => 
     JsString(Instant.ofEpochMilli(dt.value).toString) 
} 

Ist es möglich, dieses Bit außer Kraft zu setzen?

Ich habe ein Experiment erstellt ...

import java.time.Instant 
import play.api.libs.json._ 
import reactivemongo.bson._ 
import reactivemongo.play.json.BSONFormats.BSONDocumentFormat 

object Experiment { 

    // Original document (usually coming from the database) 
    val bson = BSONDocument(
     "something" -> BSONDateTime(1462288846873L) // equals "2016-05-03T15:20:46.873Z" 
    ) 

    // Reader: converts from BSONDateTime to JsString 
    implicit object BSONDateTimeToJsStringReader extends BSONReader[BSONDateTime, JsString] { 
     def read(bsonDate: BSONDateTime): JsString = { 
      JsString(Instant.ofEpochMilli(bsonDate.value).toString) 
     } 
    } 

    // Reader: converts from BSONDateTime to JsValue 
    implicit object BSONDateTimeToJsValueReader extends BSONReader[BSONDateTime, JsValue] { 
     def read(bsonDate: BSONDateTime): JsValue = { 
      JsString(Instant.ofEpochMilli(bsonDate.value).toString) 
     } 
    } 

    // Read and print specific property "something" using the `BSONReader`s above 
    def printJsDate = { 
     val jsStr: JsString = bson.getAs[JsString]("something").get 
     println(jsStr) // "2016-05-03T15:20:46.873Z" 

     val jsVal: JsValue = bson.getAs[JsValue]("something").get 
     println(jsVal) // "2016-05-03T15:20:46.873Z" 
    } 

    // Use ReactiveMongo's default format to convert a BSONDocument into a JsObject 
    def printAsJsonDefault = { 
     val json: JsObject = BSONDocumentFormat.writes(bson).as[JsObject] 
     println(json) // {"something":{"$date":1462288846873}} 
     // What I want: {"something":"2016-05-03T15:20:46.873Z"} 
    } 

} 

Ich mag würde zu beachten, dass die Umstellung auf BSONDateTime JsValue sollte immer funktionieren, wenn ich einen BSONDocument zu JSObject konvertieren, nicht nur, wenn ich manuell einem bestimmten Pick bekannte Eigenschaft. Das bedeutet, dass die Eigenschaft "etwas" in meinem Beispiel einen beliebigen Namen haben und auch in einem Unterdokument erscheinen kann.

BTW: Falls Sie sich fragen, ich arbeite normalerweise mit BSON-Sammlungen in meinem Play-Projekt, aber ich denke nicht, dass es in diesem Fall einen Unterschied macht.

bearbeiten

Ich habe versucht, eine Writes[BSONDateTime] bietet, aber leider ist es nicht verwendet wird, und ich immer noch das gleiche Ergebnis wie zuvor. Code:

import java.time.Instant 
import play.api.libs.json._ 
import reactivemongo.bson.{BSONDocument, BSONDateTime} 

object MyImplicits { 
    implicit val dateWrites = Writes[BSONDateTime] (bsonDate => 
     JsString(Instant.ofEpochMilli(bsonDate.value).toString) 
    ) 

    // I've tried this too: 
// implicit val dateWrites = new Writes[BSONDateTime] { 
//  def writes(bsonDate: BSONDateTime) = JsString(Instant.ofEpochMilli(bsonDate.value).toString) 
// } 
} 

object Experiment { 
    // Original document (usually coming from the database) 
    val bson = BSONDocument("something" -> BSONDateTime(1462288846873L)) 

    // Use ReactiveMongo's default format to convert a BSONDocument into a JsObject 
    def printAsJson = { 
     import reactivemongo.play.json.BSONFormats.BSONDocumentFormat 
     import MyImplicits.dateWrites // import is ignored 

     val json: JsObject = BSONDocumentFormat.writes(bson).as[JsObject] 
     //val json: JsValue = Json.toJson(bson) // I've tried this too 
     println(json) // {"something":{"$date":1462288846873}} 
    } 
} 

Antwort

2

Wie für jede Art wird der BSON Wert umgewandelt JSON zu spielen instances of Writes[T] verwenden.

Dort müssen Sie im impliziten Bereich Ihre eigene Writes[BSONDateTime] angeben.

import reactivemongo.bson._ 
import play.api.libs.json._ 

object MyImplicits { 
    implicit val dateWrites = Writes[BSONDateTime] { date => 
    ??? 
    } 

    def jsonDoc(doc: BSONDocument) = 
    JsObject(bson.elements.map(elem => elem._1 -> myJson(elem._2))) 

    implicit val docWrites = OWrites[BSONDocument](jsonDoc) 

    def myJson(value: BSONValue): JsValue = value match { 
    case BSONDateTime(value) = ??? 
    case doc @ BSONDocument(_) => jsonDoc(doc) 
    case bson => BSONFormats.toJSON(bson) 
    } 
} 

/* where needed */ import MyImplicits.dateWrites 
+0

Vielen Dank für Ihre Antwort! Ich habe ein 'Writes [BSONDateTime]' wie du vorgeschlagen implementiert, aber es scheint ignoriert zu werden. Siehe Bearbeiten in meiner Frage. Irgendwie habe ich das Gefühl, weil ich 'BSONDocumentFormat' verwende. Hast du eine Idee? – Nick

+0

Probieren Sie 'println (s" writer = $ {implizit [Schreibt [BSONDateTime]]} ") – cchantep

+0

Drucke:' writer = play.api.libs.json.Writes $$ anon $ 6 @ b9c81b0' – Nick

Verwandte Themen