How to de-serialize `Either` types in scala using `lift-json`?

1.2k views Asked by At

How to serialize and de-serialize Either types?

Below can be performed in the scala console with lift-json added to the classpath.

case class Thing(location: Either[Location, Address])

case class Location(latitude: Double,
                    longitude: Double)

case class Address(line1: String,
                   line2: String,
                   city: String,
                   state: String,
                   country: String,
                   postalCode: String)

import net.liftweb.json._
import Extraction._

implicit val formats = DefaultFormats

val myThing = Thing(Left(Location(0d, 0d)))
val asJson = pretty(render(decompose(myThing)))

Produces:

asJson: String =
{
  "location":{
    "a":{
      "latitude":0.0,
      "longitude":0.0
    }
  }
}

Then:

val deserialized = JsonParser.parse(asJson).extract[Thing]

Produces:

net.liftweb.json.MappingException: No usable value for location
Parsed JSON values do not match with class constructor
args=
arg types=
constructor=public scala.util.Either()
    at net.liftweb.json.Meta$.fail(Meta.scala:191)
    at net.liftweb.json.Extraction$.mkValue$1(Extraction.scala:357)
    at net.liftweb.json.Extraction$.net$liftweb$json$Extraction$$build$1(Extraction.scala:317)
    at net.liftweb.json.Extraction$$anonfun$13.apply(Extraction.scala:253)
    at net.liftweb.json.Extraction$$anonfun$13.apply(Extraction.scala:253)
    at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:244)
    at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:244)
    at scala.collection.immutable.List.foreach(List.scala:318)
    at scala.collection.TraversableLike$class.map(TraversableLike.scala:244)
    at scala.collection.AbstractTraversable.map(Traversable.scala:105)
    at net.liftweb.json.Extraction$.instantiate$1(Extraction.scala:253)
    at net.liftweb.json.Extraction$.newInstance$1(Extraction.scala:286)
    at net.liftweb.json.Extraction$.net$liftweb$json$Extraction$$build$1(Extraction.scala:315)
    at net.liftweb.json.Extraction$.net$liftweb$json$Extraction$$extract0(Extraction.scala:366)
    at net.liftweb.json.Extraction$.net$liftweb$json$Extraction$$extract0(Extraction.scala:199)
    at net.liftweb.json.Extraction$.extract(Extraction.scala:43)
    at net.liftweb.json.JsonAST$JValue.extract(JsonAST.scala:312)
    at .<init>(<console>:22)
    at .<clinit>(<console>)
    at .<init>(<console>:7)
    at .<clinit>(<console>)
    at $print(<console>)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:601)
    at scala.tools.nsc.interpreter.IMain$ReadEvalPrint.call(IMain.scala:734)
    at scala.tools.nsc.interpreter.IMain$Request.loadAndRun(IMain.scala:983)
    at scala.tools.nsc.interpreter.IMain.loadAndRunReq$1(IMain.scala:573)
    at scala.tools.nsc.interpreter.IMain.interpret(IMain.scala:604)
    at scala.tools.nsc.interpreter.IMain.interpret(IMain.scala:568)
    at scala.tools.nsc.interpreter.ILoop.reallyInterpret$1(ILoop.scala:756)
    at scala.tools.nsc.interpreter.ILoop.interpretStartingWith(ILoop.scala:801)
    at scala.tools.nsc.interpreter.ILoop.command(ILoop.scala:713)
    at scala.tools.nsc.interpreter.ILoop.processLine$1(ILoop.scala:577)
    at scala.tools.nsc.interpreter.ILoop.innerLoop$1(ILoop.scala:584)
    at scala.tools.nsc.interpreter.ILoop.loop(ILoop.scala:587)
    at scala.tools.nsc.interpreter.ILoop$$anonfun$process$1.apply$mcZ$sp(ILoop.scala:878)
    at scala.tools.nsc.interpreter.ILoop$$anonfun$process$1.apply(ILoop.scala:833)
    at scala.tools.nsc.interpreter.ILoop$$anonfun$process$1.apply(ILoop.scala:833)
    at scala.tools.nsc.util.ScalaClassLoader$.savingContextLoader(ScalaClassLoader.scala:135)
    at scala.tools.nsc.interpreter.ILoop.process(ILoop.scala:833)
    at scala.tools.nsc.interpreter.ILoop.main(ILoop.scala:900)
    at xsbt.ConsoleInterface.run(ConsoleInterface.scala:69)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:601)
    at sbt.compiler.AnalyzingCompiler.call(AnalyzingCompiler.scala:102)
    at sbt.compiler.AnalyzingCompiler.console(AnalyzingCompiler.scala:77)
    at sbt.Console.sbt$Console$$console0$1(Console.scala:23)
    at sbt.Console$$anonfun$apply$2$$anonfun$apply$1.apply$mcV$sp(Console.scala:24)
    at sbt.TrapExit$.sbt$TrapExit$$executeMain$1(TrapExit.scala:33)
    at sbt.TrapExit$$anon$1.run(TrapExit.scala:42)
Caused by: net.liftweb.json.MappingException: Parsed JSON values do not match with class constructor
args=
arg types=
constructor=public scala.util.Either()
    at net.liftweb.json.Meta$.fail(Meta.scala:191)
    at net.liftweb.json.Extraction$.instantiate$1(Extraction.scala:262)
    at net.liftweb.json.Extraction$.newInstance$1(Extraction.scala:286)
    at net.liftweb.json.Extraction$.net$liftweb$json$Extraction$$build$1(Extraction.scala:315)
    at net.liftweb.json.Extraction$.mkValue$1(Extraction.scala:351)
    ... 52 more
2

There are 2 answers

2
howard branch On

You need to explicitly enumerate the type hints, e.g.

implicit def formats = DefaultFormats + FullTypeHints(classOf[Either[Location, Address]] :: Nil)

Do note however that you'll need fix 1417, https://github.com/lift/framework/commit/ba2c444076ee6f5dc788bcaab4853b1640b5fef1 since before this constructors are memoized without generic types and deserialization of other Eithers will fail

0
fommil On

You need to explicitly enumerate the type hints, e.g.

implicit def formats = DefaultFormats +
                       FullTypeHints(classOf[Either[Location, Address]] :: Nil)