scala 使用 Play Framework 的 JSON 库自定义 JodaTime 序列化程序?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18255504/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-22 05:35:21  来源:igfitidea点击:

Custom JodaTime serializer using Play Framework's JSON library?

jsonscalaserializationdeserializationplayframework-2.1

提问by Dominykas Mostauskis

How do I implement a custom JodaTime's DateTime serializer/deserializer for JSON? I'm inclined to use the Play Framework's JSON library (2.1.1). There is a default DateTime serializer, but it uses dt.getMillisinstead of .toStringwhich would return an ISO compliant String.

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

Writing Reads[T] amd Writes[T] for case classes seems fairly straightforward, but I can't figure out how to do the same for DateTime.

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

回答by Alexey Romanov

There is a default DateTime serializer, but it uses dt.getMillis instead of .toString which would return an ISO compliant String.

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

If you look at the source, Reads.jodaDateReadsalready handles both numbers and strings using DateTimeFormatter.forPattern. If you want to handle ISO8601 string, just replace it with ISODateTimeFormat:

如果您查看源代码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))

  }

(simplify as desired, e.g. remove number handling)

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

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

回答by Evghenii Todorov

I use Play 2.3.7 and define in companion object implicit reads/writes with string pattern:

我使用 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]
}

回答by Alex

Another, perhaps simpler, solution would be to do a map, for example:

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

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

UPDATE

更新

If you had a recurring need for this conversion, then you could create your own implicit conversion, it is only a couple of lines of code:

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

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