Search code examples
javajsonscaladatetimelift-json

How to serialize and deserialize Java 8 dateTime in lift JSON?


I have a case class which I want to serialize first. Then after that, I want to deserialize it for storing purpose in MongoDB but java 8 LocalDateTime was creating problem. I took help from this link: how to deserialize DateTime in Lift but with no luck. I am unable to write it for java 8 date time. Can any one please help me with this date Time issue? Here is my code:

import net.liftweb.json.Serialization.{read, write}

implicit val formats = Serialization.formats(NoTypeHints) 

case class Child(var str: String, var Num: Int, var abc: Option[String], MyList: List[Int], val dateTime: LocalDateTime = LocalDateTime.now())
val ser = write(Child("Mary", 5, None, List(1, 2)))
println("Child class converted to string" + ser) 

var obj = read[Child](ser)
println("object of Child is " + obj)

And here is the error message printed on the console:

(run-main-0) java.lang.ArrayIndexOutOfBoundsException: 49938
java.lang.ArrayIndexOutOfBoundsException: 49938
    at com.thoughtworks.paranamer.BytecodeReadingParanamer$ClassReader.<init>(BytecodeReadingParanamer.java:451)
    at com.thoughtworks.paranamer.BytecodeReadingParanamer$ClassReader.<init>(BytecodeReadingParanamer.java:431)
    at com.thoughtworks.paranamer.BytecodeReadingParanamer$ClassReader.<init>(BytecodeReadingParanamer.java:492)
    at com.thoughtworks.paranamer.BytecodeReadingParanamer$ClassReader.<init>(BytecodeReadingParanamer.java:337)
    at com.thoughtworks.paranamer.BytecodeReadingParanamer.lookupParameterNames(BytecodeReadingParanamer.java:100)
    at com.thoughtworks.paranamer.CachingParanamer.lookupParameterNames(CachingParanamer.java:75)
    at com.thoughtworks.paranamer.CachingParanamer.lookupParameterNames(CachingParanamer.java:68)
    at net.liftweb.json.Meta$ParanamerReader$.lookupParameterNames(Meta.scala:89)
    at net.liftweb.json.Meta$Reflection$.argsInfo$1(Meta.scala:237)
    at net.liftweb.json.Meta$Reflection$.constructorArgs(Meta.scala:253)
    at net.liftweb.json.Meta$Reflection$.net$liftweb$json$Meta$Reflection$$findMostComprehensive$1(Meta.scala:266)
    at net.liftweb.json.Meta$Reflection$$anonfun$primaryConstructorArgs$1.apply(Meta.scala:269)
    at net.liftweb.json.Meta$Reflection$$anonfun$primaryConstructorArgs$1.apply(Meta.scala:269)
    at net.liftweb.json.Meta$Memo.memoize(Meta.scala:199)
    at net.liftweb.json.Meta$Reflection$.primaryConstructorArgs(Meta.scala:269)
    at net.liftweb.json.Extraction$.decompose(Extraction.scala:88)
    at net.liftweb.json.Extraction$$anonfun$1.applyOrElse(Extraction.scala:91)
    at net.liftweb.json.Extraction$$anonfun$1.applyOrElse(Extraction.scala:89)
    at scala.collection.immutable.List.collect(List.scala:305)
    at net.liftweb.json.Extraction$.decompose(Extraction.scala:89)
    at net.liftweb.json.Serialization$.write(Serialization.scala:38)
    at TestActor$.delayedEndpoint$TestActor$1(TestActor.scala:437)
    at TestActor$delayedInit$body.apply(TestActor.scala:54)
    at scala.Function0$class.apply$mcV$sp(Function0.scala:40)
    at scala.runtime.AbstractFunction0.apply$mcV$sp(AbstractFunction0.scala:12)
    at scala.App$$anonfun$main$1.apply(App.scala:76)
    at scala.App$$anonfun$main$1.apply(App.scala:76)
    at scala.collection.immutable.List.foreach(List.scala:383)
    at scala.collection.generic.TraversableForwarder$class.foreach(TraversableForwarder.scala:35)
    at scala.App$class.main(App.scala:76)
    at TestActor$.main(TestActor.scala:54)
    at TestActor.main(TestActor.scala)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)

If I remove the dateTime parameter from case class, it works fine. It seems like the problem is in dateTime.


Solution

  • I ran your code on my Intellij Idea, got the same error. Tried to debug the cause but the invocation stack is so deep that I finally gave up. But I guess maybe it is because Lift doesn't provide default Format for LocaleDateTime, just like the post you mentioned said, "it is the DateParser format that Lift uses by default."

    Here is a compromise for your reference,Lift-JSON provides default Date format for us

    // net.liftweb.json.Serialization Line 72
    def formats(hints: TypeHints) = new Formats {
      val dateFormat = DefaultFormats.lossless.dateFormat
      override val typeHints = hints
    }
    

    So instead of going all the way to write customized serializer, we may as well change our data type to fit the default Date format. Plus, net.liftweb.mongodb.DateSerializer(Line 79) provides support for Date serialization.

    Then, we can provide method to easily get LocaleDateTime. Following is how I try to figure it out.

    package jacoffee.scalabasic
    
    import java.time.{ ZoneId, LocalDateTime }
    import java.util.Date
    
    // package object defined is for Scala compiler to look for implicit conversion for case class parameter date
    package object stackoverflow {
     implicit def toDate(ldt: LocalDateTime): Date =
       Date.from(ldt.atZone(ZoneId.systemDefault()).toInstant())
    
     implicit def toLDT(date: Date): LocalDateTime =
       LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault())
    }
    
    
    package jacoffee.scalabasic.stackoverflow
    
    import java.time.LocalDateTime
    import java.util.Date
    import net.liftweb.json.{ NoTypeHints, Serialization }
    import net.liftweb.json.Serialization.{ read, write }
    
    
    case class Child(var str: String, var Num: Int, var abc: Option[String],
     myList: List[Int], val date : Date = LocalDateTime.now()) {
       def getLDT: LocalDateTime = date
     }
    
    object DateTimeSerialization extends App {
       implicit val formats = Serialization.formats(NoTypeHints)
    
       val ser = write(Child("Mary", 5, None, List(1, 2)))
    // Child class converted to string {"str":"Mary","Num":5,"myList":[1,2],"date":"2015-07-21T03:07:05.699Z"}
       println(" Child class converted to string " + ser)
    
       var obj=read[Child](ser)
       // Object of Child is Child(Mary,5,None,List(1, 2),Tue Jul 21 11:48:22 CST 2015)
       println(" Object of Child is "+ obj)
       // LocalDateTime of Child is 2015-07-21T11:48:22.075
       println(" LocalDateTime of Child is "+ obj.getLDT)
    }
    

    Anyway, hope it helps.