Как десериализовать DateTime в Lift

У меня возникли проблемы с десериализацией поля org.joda.time.DateTime из JSON в класс case.

JSON:
val ajson=parse(""" { "creationDate": "2013-01-02T10:48:41.000-05:00" }""")

Я также установил эти параметры сериализации:
implicit val formats = Serialization.formats(NoTypeHints) ++ net.liftweb.json.ext.JodaTimeSerializers.all

И десериализация:
val val1=ajson.extract[Post]

где сообщение:
case class Post(Дата создания: DateTime){ ... }

Исключение, которое я получаю:

 net.liftweb.json.MappingException: No usable value for creationDate
    Invalid date format 2013-01-02T10:48:41.000-05:00

Как я могу десериализовать эту строку даты в объект DateTime?

РЕДАКТИРОВАТЬ:
Это работает: val date3= new DateTime("2013-01-05T06:24:53.000-05:00")который использует ту же строку даты из JSON, что и при десериализации. Что тут происходит?

2 ответа

Решение

Похоже, это DateParser формат, который Lift использует по умолчанию. Копаясь в коде, вы можете увидеть, что парсер пытается использовать DateParser.parse(s, format) перед передачей результата в конструктор для org.joda.time.DateTime,

object DateParser {
  def parse(s: String, format: Formats) = 
    format.dateFormat.parse(s).map(_.getTime).getOrElse(throw new MappingException("Invalid date format " + s))
}

case object DateTimeSerializer extends CustomSerializer[DateTime](format => (
  {
    case JString(s) => new DateTime(DateParser.parse(s, format))
    case JNull => null
  },
  {
    case d: DateTime => JString(format.dateFormat.format(d.toDate))
  }
))

Формат, который, кажется, использует Lift: yyyy-MM-dd'T'HH:mm:ss.SSS'Z'

Чтобы обойти это, вы можете либо указать правильный шаблон и добавить его в параметры сериализации, либо, если вы предпочитаете, чтобы конструктор JodaTime выполнял всю работу, вы можете создать свой собственный сериализатор, например:

case object MyDateTimeSerializer extends CustomSerializer[DateTime](format => (
  {
    case JString(s) => tryo(new DateTime(s)).openOr(throw new MappingException("Invalid date format " + s))
    case JNull => null
  },
  {
    case d: DateTime => JString(format.dateFormat.format(d.toDate))
  }
))

А затем добавить это в свой список форматов, а не net.liftweb.json.ext.JodaTimeSerializers.all

Не возможно на 100% элегантно, но это всего лишь несколько строк, вполне читабельно и работает:

val SourISODateTimeFormat = DateTimeFormat.forPattern("YYYY-MM-dd'T'HH:mm:ss.SSSZ")
val IntermediateDateTimeFormat = DateTimeFormat.forPattern("YYYY-MM-dd'T'HH:mm:ss'Z'")

def transformTimestamps(jvalue: JValue) = jvalue.transform {
  case JField(name @ ("createdTime" | "updatedTime"), JString(value)) =>
    val dt = SourceISODateTimeFormat.parseOption(value).get
    JField(name, JString(IntermediateDateTimeFormat.print(dt)))
}
Другие вопросы по тегам