how to deserialize DateTime in Lift
Asked Answered
H

2

5

I am having trouble deserializing a org.joda.time.DateTime field from JSON into a case class.

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

I also set these serialization options:
implicit val formats = Serialization.formats(NoTypeHints) ++ net.liftweb.json.ext.JodaTimeSerializers.all

And the deserialization:
val val1=ajson.extract[Post]

where Post is:
case class Post(creationDate: DateTime){ ... }

The exception I get is:

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

How can I deserialize that date string into a DateTime object?

EDIT:
This works: val date3= new DateTime("2013-01-05T06:24:53.000-05:00") which uses the same date string from the JSON as in the deserialization. What's happening here?

Haakon answered 12/4, 2013 at 21:0 Comment(0)
K
10

Seems like it is the DateParser format that Lift uses by default. In digging into the code, you can see that the parser attempts to use DateParser.parse(s, format) before passing the result to the constructor for 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))
  }
))

The format that Lift seems to be using is: yyyy-MM-dd'T'HH:mm:ss.SSS'Z'

To get around that, you could either specify the correct pattern and add that to your serialization options, or if you would prefer to just have the JodaTime constructor do all the work, you could create your own serializer like:

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

And then add that to your list of formats, instead of net.liftweb.json.ext.JodaTimeSerializers.all

Kindergarten answered 14/4, 2013 at 7:57 Comment(4)
I have 2 follow-up questions: (1) is this "YYYY-MM-dd'T'HH:mm:ss.SSS'-'Z" my format for dates like "2013-01-02T10:48:41.000-05:00" (2) is this the correct way to add my format to formats: `implicit val formats = Serialization.formats(NoTypeHints) ++ MyDateTimeSerializerHaakon
figured out (2): wrap MyDateTimeSerializer in a list: for those interested it looks like this: implicit val formats = Serialization.formats(NoTypeHints) ++ List(MyDateTimeSerializer)Haakon
I'm not super familiar with all the intricacies of DateFormat patterns, but what you suggest looks close. Two things, the year should be lowercase y and the '-' is part of the timezone (IE -5 hours from UTC), so you should probably remove that. yyyy-MM-dd'T'HH:mm:ss.SSSZ Is probably close to what you are looking for, but that may have an issue with the : in the TZ. As for adding the custom serializer, wrapping it in the List should work for you.Kindergarten
ok I went with (2) and it's not working, but I think the parsing is fine, but the JSON string may have some funky chars in it which need to be escaped. I deduced by because this works new DateTime(parse(""" { "creationDate": "2013-01-05T06:24:53.000-05:00" }""").extract[Post3].creationDate) but when the JSON is given as input by user it doesn't work val date3= new DateTime(parse(Text(name).toString()).extract[Post3].creationDate)Haakon
B
0

Not perhaps 100% elegant but is just a few lines, quite readable and works:

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)))
}
Brittnee answered 31/3, 2014 at 8:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.