Datetime deserialization is not working (serialization works fine)

1,664 views
Skip to first unread message

Anurag Laddha

unread,
Jun 11, 2014, 4:12:33 AM6/11/14
to lif...@googlegroups.com
Greetings,

I want java Date object to be serialized using following format: "yyyy-MM-dd'T'HH:mm:ss.SSSZ" and read back from a string of the same format during deserialization
To achieve this I've written following custom serializer

What is working : serialization
What is not working : deserialization  

import net.liftweb.json.JsonDSL._
import net.liftweb.json.JsonAST.{JObject, JField, JValue}
import net.liftweb.json._
import java.util.Date
import java.text.{ParseException, Format, SimpleDateFormat}

class DateSerializer extends Serializer[Date] {
    private val DateClass = classOf[Date]

    val dateTimeZoneFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ")      // example: 2014-06-11T13:13:57.884+0530

    def deserialize(implicit format: Formats) : PartialFunction[(TypeInfo, JValue), Date] = {
      case (TypeInfo(DateClass, _), json) => json match {
        case JString(dateString) => {
          try{
            println("reached till parse")     //this println is never called
            dateTimeZoneFormat.parse(dateString)
          }
          catch {
            case e: ParseException => throw new MappingException("Can't convert " + dateString + " to Date")
          }
        }
        case x => throw new MappingException("Can't convert " + x + " to Date")
      }
    }

    def serialize(implicit format: Formats) : PartialFunction[Any, JValue] = {
      case x: Date => JString(dateTimeZoneFormat.format(x))
    }
  }



import net.liftweb.json._
import org.apache.commons.lang3.StringUtils
import net.liftweb.json.Extraction._
import scala.reflect.Manifest

object JsonUtil {
  implicit val formats =  DefaultFormats + new DateSerializer

  def toJSONString(obj: Any): String = {
    compact(JsonAST.render(decompose(obj)))
  }

  def fromJSONString[A: Manifest](jsonString: String): Option[A] = {
    StringUtils.isBlank(jsonString) match {
      case true => None
      case false => Some(parse(jsonString).extract[A])
    }
  }
}

Model:
case class SomeDate(date : Date)

Serializing: (this works fine)
println(JsonUtil.toJSONString(SomeDate(new Date)))

output: {"date":"2014-06-11T13:13:57.884+0530"}      --> serialization works as expected

Deserialization: 
println(JsonUtil.fromJSONString[SomeDate]("""{"date":"2014-06-11T13:13:57.884+0530"}"""))        --> same date string that was serialized in the previous call

I get following exception:
net.liftweb.json.MappingException: No usable value for date
Invalid date '2014-06-11T13:18:29.379+0530'
at net.liftweb.json.Meta$.fail(Meta.scala:190)
at net.liftweb.json.Extraction$.mkValue$1(Extraction.scala:356)
at net.liftweb.json.Extraction$.build$1(Extraction.scala:316)
at net.liftweb.json.Extraction$$anonfun$12.apply(Extraction.scala:252)
at net.liftweb.json.Extraction$$anonfun$12.apply(Extraction.scala:252)

Deserialization with another date format: //date json string confirms to following format: "yyyy-MM-dd'T'HH:mm:ss'Z'"
println(JsonUtil.fromJSONString[SomeDate]("""{"date":"2014-06-10T22:56:22Z"}"""))
output: Some(SomeDate(Wed Jun 11 04:26:22 GMT+05:30 2014))

Deserialization works if date json confirms to following format:  "yyyy-MM-dd'T'HH:mm:ss'Z'" but I've specified a different format in my deserializer: "yyyy-MM-dd'T'HH:mm:ss.SSSZ"

The deserializer I've written doesnt even seem that its getting called.
What wrong am i doing? How this can issue be resolved?

Thanks!

Antonio Salazar Cardozo

unread,
Jun 11, 2014, 5:07:15 PM6/11/14
to lif...@googlegroups.com
I haven't gotten a chance to look at this deeply, but I believe by default lift-json has a special case
for date serializer/deserializers since they're such a common need; see the dateFormat def in Formats.
Unfortunately, in what seems a rather odd oversight, the only way to construct a format with the
dateFormat set is to create your own Format subclass; e.g.:

implicit val formats = new DefaultFormats {
  override def formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ")
}

The upside is this is significantly less code than you've had to write until now :)
Thanks,
Antonio

Tim Nelson

unread,
Jun 11, 2014, 6:01:56 PM6/11/14
to lif...@googlegroups.com
You could also use DefaultFormats.lossless, which gives you a DateFormatter like this.

Anurag Laddha

unread,
Jun 12, 2014, 6:41:39 AM6/12/14
to lif...@googlegroups.com
Thanks Antonio and Tim

I tried your suggestion:

 implicit val formats =  new DefaultFormats {
    override def dateFormatter = {
      val df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ")
      df.setTimeZone(TimeZone.getTimeZone("Asia/Calcutta"))
      df
    }
  }

Still there is an issue

The date always gets serialized in UTC timezone regardless of me specifying the timezone of my choice
The date gets deserialized back to local time zone (this is as per my requirement)

println(JsonUtil.toJSONString(SomeDate(new java.util.Date)))   --> gives output {"date":"2014-06-12T10:30:09.911+0000"}   --> this is in UTC timezone

println(JsonUtil.fromJSONString[SomeDate]( """{"date":"2014-06-11T13:18:29.379+0530"}""") --> output: Some(SomeDate(Wed Jun 11 13:18:29 GMT+05:30 2014)) --> this is correct behavior.

How can i control timezone when data is being serialized?

Thanks!
Anurag

Antonio Salazar Cardozo

unread,
Jun 12, 2014, 1:59:57 PM6/12/14
to lif...@googlegroups.com
Ah, yes. Looks like the default DateFormat that uses the DateFormatter always forces the timezone to UTC. This
is actually reasonable behavior most of the time (helps with a lot of comparisons), but to override it rather than just
overriding dateFormatter, also override DateFormat with something like:

  override val dateFormat = new DateFormat {
    def parse(s: String) = try {
      Some(dateFormatter.parse(s))
    } catch {
      case e: ParseException => None
    }

    def format(d: Date) = dateFormatter.format(d)
  }

Maybe. I haven't had to deal with timezones in formatters before; you may instead have to
for how this is originally defined.
Thanks,
Antonio

Anurag Laddha

unread,
Jun 15, 2014, 2:53:08 PM6/15/14
to lif...@googlegroups.com
This worked! Thanks a bunch!

Antonio Salazar Cardozo

unread,
Jun 15, 2014, 8:06:33 PM6/15/14
to lif...@googlegroups.com
Awesome! Glad it worked!
Thanks,
Antonio
Reply all
Reply to author
Forward
0 new messages