2016-05-04 3 views
0

Я использую ReactiveMongo 0.11.11 для Play 2.5 и хочу преобразовать BSONDocument в JsObject.BSONDocument to JsObject и переопределить BSONDateTimeFormat

Для большинства типов данных BSON (String, Int ...) значения по умолчанию отлично подходят для работы библиотеки. Для типа BSON DateTime (BSONDateTime) значение свойства JSON не дает мне формат, который мне нужен.

Значение JSON для даты является JSObject с именем свойства $date и метку времени UNIX в миллисекундах, как его значение:

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

JSON Я хочу, чтобы это строковое представление Дата вроде этого:

{ 
    "something": "2016-05-03T15:20:46.873Z" 
} 

К сожалению, я не знаю, как переопределить поведение по умолчанию, не переписывая все или не меняя код в самой библиотеке.

Это где я думаю, что это происходит (source code):

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

Моя версия бы выглядеть следующим образом:

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

Можно ли переопределить этот бит?

Я создал эксперимент ...

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

} 

Я хотел бы отметить, что преобразование BSONDateTime в JsValue всегда должен работать, когда я конвертировать BSONDocument в JSObject, а не только тогда, когда я вручную выбрать конкретный известное свойство. Это означает, что свойство «что-то» в моем примере может иметь любое имя и также отображаться в поддоку.

BTW: В случае, если вы задаетесь вопросом, я вообще работаю с коллекциями BSON в своем проекте Play, но я не думаю, что это имеет значение в этом случае.

Редактировать

Я попытался предоставления Writes[BSONDateTime], но, к сожалению, не используется, и я до сих пор получить тот же результат, как и раньше. Код:

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

ответ

2

Как и для любого типа, значение BSON преобразуются в Play JSON с помощью instances of Writes[T].

Там вам необходимо предоставить в неявной сфере свои собственные Writes[BSONDateTime].

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

Большое спасибо за ваш ответ! Я применил 'Writes [BSONDateTime]', как вы предлагали, но, похоже, его игнорируют. См. Редактирование в моем вопросе. Я как-то чувствую, что это потому, что я использую 'BSONDocumentFormat'. У тебя есть идеи? – Nick

+0

Попробуйте 'println (s" writer = $ {неявно [Записывает [BSONDateTime]]} ")' – cchantep

+0

Отпечатки: 'writer = play.api.libs.json.Writes $$ anon $ 6 @ b9c81b0' – Nick

 Смежные вопросы

  • Нет связанных вопросов^_^