Pergunta

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

Foi útil?

Solução

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.

Outras dicas

You could add an implicit conversion:

implicit def listToVect(list:List[Bar]):Vector[Bar] = list.map(identity)(breakOut)

after that, Serialization.read[Foo] works as expected.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top