【问题标题】:Custom JodaTime serializer using Play Framework's JSON library?使用 Play Framework JSON 库的自定义 Joda 时间序列化器?
【发布时间】:2013-08-17 19:24:38
【问题描述】:

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

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

【问题讨论】:

    标签: json scala serialization deserialization playframework-2.1


    【解决方案1】:

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

    如果您查看the sourceReads.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())
      }
    

    【讨论】:

    • 很好的答案。我必须进行修改才能使其正常工作。 'pattern' 被提及但不在范围内。我用'df'代替了它。这是你的意图吗?
    • @justinhj 谢谢! df 实际上在那里没有意义。我修好了。
    【解决方案2】:

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

    【讨论】:

    • 嗨,我试过你的代码,但它对我不起作用,我已经为这个问题创建了一个新的post
    • 在您的代码中使用Json.fromJson[User](value) 而不是Json.fromJson(value)。更多详情请参阅answer 到您的帖子。
    • 您的意思是"yyyy-MM-dd'T'HH:mm:ssZ" 而不是"yyyy-MM-dd'T'HH:mm:ss'Z'"
    • @JakeGreene:嗯,这取决于您项目中定义的时间字符串格式。在我的应用程序中,我使用了"yyyy-MM-dd'T'HH:mm:ss'Z'"
    • 是的,这对我也不起作用,我不得不使用@Guillaume 帖子中的代码
    【解决方案3】:

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-12-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-07-12
      • 1970-01-01
      相关资源
      最近更新 更多