Пользовательский сериализатор JodaTime, используя библиотеку JSON Play Framework?
Как реализовать пользовательский сериализатор/десериализатор JTime DateTime для JSON? Я склонен использовать библиотеку JSON Play Framework (2.1.1). Существует сериализатор по умолчанию DateTime, но он использует dt.getMillis
вместо .toString
, который возвращает строку, совместимую с ISO.
Запись чтения [T] amd Записи [T] для классов case выглядят довольно просто, но я не могу понять, как сделать то же самое для DateTime.
Ответы
Ответ 1
Я использую 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]
}
Ответ 2
Существует сериализатор по умолчанию 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())
}
Ответ 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)
}
UPDATE
Если у вас возникла повторяющаяся потребность в этом преобразовании, вы можете создать свое собственное неявное преобразование, это всего лишь пара строк кода:
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())
)
)