Custom JodaTime serializer using Play Framework's JSON library?

前端 未结 3 532
难免孤独
难免孤独 2020-12-28 08:35

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 DateTi

相关标签:
3条回答
  • 2020-12-28 09:07

    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())
      )
    )
    
    0 讨论(0)
  • 2020-12-28 09:23

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

    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]
    }
    
    0 讨论(0)
  • 2020-12-28 09:28

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

    If you look at the source, Reads.jodaDateReads already handles both numbers and strings using DateTimeFormatter.forPattern. If you want to handle ISO8601 string, just replace it with 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())
      }
    
    0 讨论(0)
提交回复
热议问题