17

如何为 JSON 实现自定义 JodaTime 的 DateTime 序列化器/反序列化器?我倾向于使用 Play Framework 的 JSON 库(2.1.1)。有一个默认的 DateTime 序列化程序,但它使用dt.getMillis而不是.toString返回一个符合 ISO 标准的字符串。

为案例类编写 Reads[T] amd Writes[T] 似乎相当简单,但我不知道如何为 DateTime 做同样的事情。

4

3 回答 3

25

有一个默认的 DateTime 序列化程序,但它使用 dt.getMillis 而不是 .toString,这将返回符合 ISO 标准的字符串。

如果您查看源代码Reads.jodaDateReads已经使用DateTimeFormatter.forPattern. 如果要处理 ISO8601 字符串,只需将其替换为ISODateTimeFormat

  implicit val jodaISODateReads: Reads[org.joda.time.DateTime] = new Reads[org.joda.time.DateTime] {
    import org.joda.time.DateTime

    val df = org.joda.time.format.ISODateTimeFormat.dateTime()

    def reads(json: JsValue): JsResult[DateTime] = json match {
      case JsNumber(d) => JsSuccess(new DateTime(d.toLong))
      case JsString(s) => parseDate(s) match {
        case Some(d) => JsSuccess(d)
        case None => JsError(Seq(JsPath() -> Seq(ValidationError("validate.error.expected.date.isoformat", "ISO8601"))))
      }
      case _ => JsError(Seq(JsPath() -> Seq(ValidationError("validate.error.expected.date"))))
    }

    private def parseDate(input: String): Option[DateTime] =
      scala.util.control.Exception.allCatch[DateTime] opt (DateTime.parse(input, df))

  }

(根据需要进行简化,例如删除号码处理)

  implicit val jodaDateWrites: Writes[org.joda.time.DateTime] = new Writes[org.joda.time.DateTime] {
    def writes(d: org.joda.time.DateTime): JsValue = JsString(d.toString())
  }
于 2013-08-16T06:11:45.773 回答
19

我使用 Play 2.3.7 并在伴随对象中使用字符串模式定义隐式读/写:

case class User(username:String, birthday:org.joda.time.DateTime)

object User {
  implicit val yourJodaDateReads = Reads.jodaDateReads("yyyy-MM-dd'T'HH:mm:ss'Z'")
  implicit val yourJodaDateWrites = Writes.jodaDateWrites("yyyy-MM-dd'T'HH:mm:ss'Z'")
  implicit val userFormat = Json.format[User]
}
于 2015-02-18T15:01:22.887 回答
9

另一个可能更简单的解决方案是制作地图,例如:

case class GoogleDoc(id: String, etag: String, created: LocalDateTime)

object GoogleDoc {
  import org.joda.time.LocalDateTime
  import org.joda.time.format.ISODateTimeFormat

  implicit val googleDocReads: Reads[GoogleDoc] = (
      (__ \ "id").read[String] ~
      (__ \ "etag").read[String] ~
      (__ \ "createdDate").read[String].map[LocalDateTime](x => LocalDateTime.parse(x, ISODateTimeFormat.basicdDateTime()))
  )(GoogleDoc)
}

更新

如果您经常需要这种转换,那么您可以创建自己的隐式转换,只需几行代码:

import org.joda.time.LocalDateTime
import org.joda.time.format.ISODateTimeFormat

implicit val readsJodaLocalDateTime = Reads[LocalDateTime](js =>
  js.validate[String].map[LocalDateTime](dtString =>
    LocalDateTime.parse(dtString, ISODateTimeFormat.basicDateTime())
  )
)
于 2013-09-18T05:51:38.480 回答