как десериализовать 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(creationDate: 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, что и при десериализации. Что тут происходит?


person Adrian    schedule 12.04.2013    source источник


Ответы (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

person jcern    schedule 14.04.2013
comment
У меня есть 2 дополнительных вопроса: (1) это ГГГГ-ММ-дд'Т'ЧЧ:мм:сс.ССС'-'Z мой формат для дат, таких как 2013-01-02T10:48:41.000-05:00 (2) это правильный способ добавить мой формат в форматы: `неявные форматы val = Serialization.formats(NoTypeHints) ++ MyDateTimeSerializer - person Adrian; 15.04.2013
comment
разобрался (2): обернуть MyDateTimeSerializer в список: для интересующихся это выглядит так: implicit val formats = Serialization.formats(NoTypeHints) ++ List(MyDateTimeSerializer) - person Adrian; 15.04.2013
comment
Я не очень хорошо знаком со всеми тонкостями шаблонов DateFormat, но то, что вы предлагаете, выглядит близко. Две вещи: год должен быть в нижнем регистре y, а «-» является частью часового пояса (IE -5 часов от UTC), поэтому вам, вероятно, следует удалить это. yyyy-MM-dd'T'HH:mm:ss.SSSZ Вероятно, это близко к тому, что вы ищете, но это может быть проблема с : в TZ. Что касается добавления пользовательского сериализатора, его обертка в List должна работать на вас. - person jcern; 15.04.2013
comment
хорошо, я выбрал (2), и это не работает, но я думаю, что синтаксический анализ в порядке, но в строке JSON могут быть некоторые странные символы, которые необходимо экранировать. Я сделал вывод, потому что это работает new DateTime(parse(""" { "creationDate": "2013-01-05T06:24:53.000-05:00" }""").extract[Post3].creationDate), но когда пользователь вводит JSON, он не работает val date3= new DateTime(parse(Text(name).toString()).extract[Post3].creationDate) - person Adrian; 15.04.2013

Возможно, не на 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)))
}
person Erik Kaplun    schedule 31.03.2014