How can i deserialize json array using lift-json to scala vector?
For example:
case class Foo(bar: Vector[Bar])
trait Bar {
def value: Int
}
case class Bar1(value: Int) extends Bar
case class Bar2(value: Int) extends Bar
import net.liftweb.json.{ShortTypeHints, Serialization, DefaultFormats}
implicit val formats = new DefaultFormats {
override val typeHintFieldName = "type"
override val typeHints = ShortTypeHints(List(classOf[Foo],classOf[Bar1],classOf[Bar2]))
}
println(Serialization.writePretty(Foo(Vector(Bar1(1), Bar2(5), Bar1(1)))))
The result is:
{
"type":"Foo",
"bar":[{
"type":"Bar1",
"value":1
},{
"type":"Bar2",
"value":5
},{
"type":"Bar1",
"value":1
}]
}
Good. But when i try to deserialize this string
println(Serialization.read[Foo](Serialization.writePretty(Foo(Vector(Bar1(1), Bar2(5), Bar1(1))))))
i get an exception:
net.liftweb.json.MappingException: Parsed JSON values do not match with class constructor args=List(Bar1(1), Bar2(5), Bar1(1)) arg types=scala.collection.immutable.$colon$colon constructor=public test.Foo(scala.collection.immutable.Vector)
It's means that json array associated with scala list, not vector type that defined in class Foo. I know that there is way to create custom serializer by extending net.liftweb.json.Serializer and include it to formats value. But how can i restore type of objects that stores in Vector. I wanna get result of deserializing like this:
Foo(Vector(Bar1(1), Bar2(5), Bar1(1)))
I've often been annoyed by the List
-centricness of Lift, and have found myself needing to do similar things in the past. The following is the approach I've used, adapted a bit for your example:
trait Bar { def value: Int }
case class Bar1(value: Int) extends Bar
case class Bar2(value: Int) extends Bar
case class Foo(bar: Vector[Bar])
import net.liftweb.json._
implicit val formats = new DefaultFormats { outer =>
override val typeHintFieldName = "type"
override val typeHints =
ShortTypeHints(classOf[Bar1] :: classOf[Bar2] :: Nil) +
new ShortTypeHints(classOf[Foo] :: Nil) {
val FooName = this.hintFor(classOf[Foo])
override def deserialize = {
case (FooName, foo) => foo \ "bar" match {
case JArray(bars) => Foo(
bars.map(_.extract[Bar](outer, manifest[Bar]))(collection.breakOut)
)
case _ => throw new RuntimeException("Not really a Foo.")
}
}
}
}
Kind of ugly, and could probably be cleaned up a bit, but it works.