Scala - Ignore case class field when decoding JSON - json

I'm playing with the example ADT in the circe documentation to reproduce an issue that I have with JSON decoding.
To achieve that, I'm using ShapesDerivation :
scala> object ShapesDerivation {
|
| implicit def encodeAdtNoDiscr[Event, Repr <: Coproduct](implicit
| gen: Generic.Aux[Event, Repr],
| encodeRepr: Encoder[Repr]
| ): Encoder[Event] = encodeRepr.contramap(gen.to)
|
| implicit def decodeAdtNoDiscr[Event, Repr <: Coproduct](implicit
| gen: Generic.Aux[Event, Repr],
| decodeRepr: Decoder[Repr]
| ): Decoder[Event] = decodeRepr.map(gen.from)
|
| }
defined object ShapesDerivation
The ADT to decode is composed by two values : a simple case class and another one that I have dedicated Encoder / Decoder (to reproduce in minimal example the issue that I really have) :
scala> :paste
// Entering paste mode (ctrl-D to finish)
sealed trait Event
object Event {
case class Foo(i: Int) extends Event
case class Bar(f : FooBar) extends Event
case class FooBar(x : Int)
implicit val encoderFooBar : Encoder[FooBar] = new Encoder[FooBar] {
override def apply(a: FooBar): Json = Json.obj(("x", Json.fromInt(a.x)))
}
implicit val decodeFooBar: Decoder[FooBar] = new Decoder[FooBar] {
override def apply(c: HCursor): Result[FooBar] =
for {
x <- c.downField("x").as[Int]
} yield FooBar(x)
}
}
Then when I try to decode a simple value like this, it's working well :
scala> import ShapesDerivation._
import ShapesDerivation._
scala> decode[Event](""" { "i" : 10 }""")
res1: Either[io.circe.Error,Event] = Right(Foo(10))
But if I tried to decode something that should be a Bar that contains a Foobar, I get a decoding failure :
scala> decode[Event](""" { "x" : 10 }""")
res2: Either[io.circe.Error,Event] = Left(DecodingFailure(CNil, List()))
But this one works because I explicitely put the case class field name :
scala> decode[Event](""" { "f" : { "x" : 10 }}""")
res7: Either[io.circe.Error,Event] = Right(Bar(FooBar(10)))
I don't what to put the case class field, directly the JSON but I think it's not possible to achieve a such behaviour. The reason why I think it's impossible is how it will know to match the good case class if there is not the field but I want to be sure that there is no way with circe to do that

Here's how you do it using just semi-auto derivation.
import io.circe.Decoder.Result
import io.circe.{Decoder, Encoder, HCursor, Json}
import io.circe.parser._
import io.circe.generic.semiauto._
object Example extends App {
sealed trait Event
object Event {
case class Foo(i: Int) extends Event
object Foo {
implicit val decoder: Decoder[Foo] = deriveDecoder
}
case class Bar(f: FooBar) extends Event
object Bar {
implicit val decoder: Decoder[Bar] = Decoder[FooBar].map(Bar.apply)
}
implicit val decoder: Decoder[Event] = Decoder[Foo].widen.or(Decoder[Bar].widen)
}
case class FooBar(x: Int)
object FooBar {
implicit val encoderFooBar: Encoder[FooBar] = deriveEncoder
implicit val decodeFooBar: Decoder[FooBar] = deriveDecoder
}
println(decode[Event](""" { "x" : 10 }"""))
}
Outputs
Right(Bar(FooBar(10)))
It gets a bit noisy with the explicit decoders, but if you care about compilation speed, it's the way to go since you'll only derive decoders once.

Related

How to serialize and deserialize traits, to and from Json, in Scala?

First Attempt:
So far I have tried spray-json. I have:
trait Base
case class A ( id: String) extends Base
case class B (id: String) extends Base
Now, for serializing and deserializing my Base type, I have the code:
implicit object BaseFormat extends RootJsonFormat[Base]{
def write(obj: Base): JsValue = {
obj match {
case a: A => a.toJson
case b: B => b.toJson
case unknown # _ => serializationError(s"Marshalling issue with ${unknown}")
}
}
def read(json: JsValue): Base = {
//how to know whether json is encoding an A or a B?
}
}
The problem is that, for implementing the read method for deserialization, I can't figure out a way to know whether the JsValue is encoding an A or a B.
Second Attempt:
For solving this in spray-json, I ended up simply renaming the field id in A to aID, and in B to bID.
Third Attempt:
Since spray-json was not as sophisticated as the alternative libraries such as zio-json or circe, which handle this issue by themselves without additional code, I started using zio-json
Now I get the error
magnolia: could not infer DeriveJsonEncoder.Typeclass for type
for all the case classes taking type parameters.
Also, it has problems with chained trait inheritance. It seems like circe uses magnolia, too. So it’s likely this would be replicated with circe, as well.
Any help would be appreciated.
You should address this problem using a json encoding/decoding library.
Here is an example using circe and it's semi-automatic mode.
Since you mentionned in the comments that you are struggling with generic types in your case classes, I'm also including that. Basically, to derive an encoder or decoder for a class Foo[T] that contains a T, you have to prove that there is a way to encode and decode T. This is done by asking for an implicit Encoder[T] and Decoder[T] where you derive Encoder[Foo[T]] and Decoder[Foo[T]]. You can generalize this reasoning to work with more than one generic type of course, you just have to have an encoder/decoder pair implicitly available where you derive the encoder/decoder for the corresponding case class.
import io.circe._, io.circe.generic.semiauto._, io.circe.syntax._
case class Foo[T](a: Int, b: String, t: T)
object Foo {
implicit def decoder[T: Encoder: Decoder]: Decoder[Foo[T]] = deriveDecoder
implicit def encoder[T: Encoder: Decoder]: Encoder[Foo[T]] = deriveEncoder
}
case class Bar(a: Int)
object Bar {
implicit val encoder: Encoder[Bar] = deriveEncoder
implicit val decoder: Decoder[Bar] = deriveDecoder
}
case class Baz(a: Int)
println(Foo(42, "hello", 23.4).asJson) // Works because circe knows Encoder[Float]
println(Foo(42, "hello", Bar(42)).asJson) // Works because we defined Encoder[Bar]
//println(Foo(42, "hello", Baz(42)).asJson) // Doesn't compile: circe doesn't know Encoder[Baz] in semi-auto mode
Try it live on Scastie
Note that a different encoder/decoder for Foo[T] needs to be generated for each type T that you are using, which is why the encoder and decoder derivation for Foo have to be methods, not values like for Bar.
There is also a fully-automatic mode, but it tends to produce compile-time errors that are harder to debug for beginner, so I would start with semi-auto. Another problem, is that auto-mode can take much longer to compile on large projects. If you're feeling adventurous, it's even more beautiful when you don't make mistakes!
import io.circe._, io.circe.generic.auto._, io.circe.syntax._
case class Foo[T](a: Int, b: String, t: T)
case class Bar(a: Int)
case class Baz(a: Int)
println(Foo(42, "hello", 23.4).asJson) // circe knows Encoder[Float] and automatically derives Encoder[Foo[Float]]
println(Foo(42, "hello", Bar(42)).asJson) // circe wants Encoder[Foo[Bar]], so it ma(cro)gically derives Encoder[Bar] and then Encoder[Foo[Bar]]
println(Foo(42, "hello", Baz(42)).asJson) // Does compile this time, circe automatically find the encoder/decoder for Baz the same way it does for Bar
Try it live on Scastie
A working example using traits with generics is this:
import io.circe.Decoder.Result
import io.circe._
import io.circe.generic.semiauto._
import io.circe.syntax._
trait MyBase[T <: MyBase[T]] {
def myid: String
def issue: MyBaseIssue
def summary: MyBaseSummary[T]
}
trait MyBaseIssue
object MyBaseIssue {
implicit val decodeIssue: Decoder[MyBaseIssue] =
Decoder[SimpleBaseIssue].map[MyBaseIssue](identity).or(
Decoder[SophisticatedBaseIssue].map[MyBaseIssue](identity)
)
implicit val encodeIssue: Encoder[MyBaseIssue] = Encoder.instance {
case simple # SimpleBaseIssue(_) => simple.asJson
case sophisticated # SophisticatedBaseIssue(_) => sophisticated.asJson
}
}
trait MyBaseSummary[T <: MyBase[T]]
object MyBaseSummary {
implicit def decodeSummary[T <: MyBase[T]: Encoder: Decoder]
: Decoder[MyBaseSummary[T]] =
Decoder[SimpleBaseSummary].map[MyBaseSummary[SimpleBase]](identity).asInstanceOf[Decoder[MyBaseSummary[T]]].or(
Decoder[SophisticatedBaseSummary].map[MyBaseSummary[SophisticatedBase]](identity).asInstanceOf[Decoder[MyBaseSummary[T]]]
)
implicit def encodeSummary[T <: MyBase[T]: Encoder: Decoder]
: Encoder[MyBaseSummary[T]] = Encoder.instance {
case simple # SimpleBaseSummary(_) => simple.asJson
case sophisticated # SophisticatedBaseSummary(_) => sophisticated.asJson
}
}
case class SimpleBase(
myid: String,
override val issue: SimpleBaseIssue,
override val summary: SimpleBaseSummary
) extends MyBase[SimpleBase]
case class SophisticatedBase(
myid: String,
extraField: String,
override val issue: SophisticatedBaseIssue,
override val summary: SophisticatedBaseSummary
) extends MyBase[SophisticatedBase]
object SimpleBase {
implicit val encoder: Encoder[SimpleBase] = deriveEncoder
implicit val decoder: Decoder[SimpleBase] = deriveDecoder
}
object SophisticatedBase {
implicit val encoder: Encoder[SophisticatedBase] = deriveEncoder
implicit val decoder: Decoder[SophisticatedBase] = deriveDecoder
}
case class SimpleBaseIssue(simpleIssueType: String) extends MyBaseIssue
case class SophisticatedBaseIssue(sophisticatedIssueType: String) extends MyBaseIssue
object SimpleBaseIssue {
implicit val encoder: Encoder[SimpleBaseIssue] = deriveEncoder
implicit val decoder: Decoder[SimpleBaseIssue] = deriveDecoder
}
object SophisticatedBaseIssue {
implicit val encoder: Encoder[SophisticatedBaseIssue] = deriveEncoder
implicit val decoder: Decoder[SophisticatedBaseIssue] = deriveDecoder
}
case class SimpleBaseSummary(simpleSummary: String) extends MyBaseSummary[SimpleBase]
case class SophisticatedBaseSummary(sophisticatedSummary: String) extends MyBaseSummary[SophisticatedBase]
object SimpleBaseSummary {
implicit val encoder: Encoder[SimpleBaseSummary] = deriveEncoder
implicit val decoder: Decoder[SimpleBaseSummary] = deriveDecoder
}
object SophisticatedBaseSummary {
implicit val encoder: Encoder[SophisticatedBaseSummary] = deriveEncoder
implicit val decoder: Decoder[SophisticatedBaseSummary] = deriveDecoder
}
case class MyBaseList[T <: MyBase[T]](
myid: String,
var membersMap: Map[String, T] = Map.empty,
override val issue: MyBaseListIssues[T],
override val summary: MyBaseListSummary[T]
) extends MyBase[MyBaseList[T]]
object MyBaseList {
implicit def membersMapDecoder[T <: MyBase[T]: Encoder: Decoder]
: Decoder[Map[String, T]] = Decoder.decodeMap
implicit def membersMapEncoder[T <: MyBase[T]: Encoder: Decoder]
: Encoder[Map[String, T]] = Encoder.encodeMap
implicit def encoder[T <: MyBase[T]: Encoder: Decoder]
: Encoder[MyBaseList[T]] = deriveEncoder
implicit def decoder[T <: MyBase[T]: Encoder: Decoder]
: Decoder[MyBaseList[T]] = deriveDecoder
}
case class MyBaseListIssues[T <: MyBase[T]](
issue: String,
var set: Set[MyBaseIssue] = Set[MyBaseIssue]()
) extends MyBaseIssue
object MyBaseListIssues {
implicit def membersMapDecoder: Decoder[Set[MyBaseIssue]] =
Decoder.decodeSet[MyBaseIssue]
implicit def membersMapEncoder: Encoder[Set[MyBaseIssue]] = Encoder.encodeSet
implicit def encoder[T <: MyBase[T]: Encoder: Decoder]
: Encoder[MyBaseListIssues[T]] = deriveEncoder
implicit def decoder[T <: MyBase[T]: Encoder: Decoder]
: Decoder[MyBaseListIssues[T]] = deriveDecoder
}
case class MyBaseListSummary[T <: MyBase[T]](
summaryList: Map[String, MyBaseSummary[T]]
) extends MyBaseSummary[MyBaseList[T]]
object MyBaseListSummary {
implicit def membersMapDecoder[T <: MyBase[T]: Encoder: Decoder]
: Decoder[Map[String, MyBaseSummary[T]]] = Decoder.decodeMap
implicit def membersMapEncoder[T <: MyBase[T]: Encoder: Decoder]
: Encoder[Map[String, MyBaseSummary[T]]] = Encoder.encodeMap
implicit def encoder[T <: MyBase[T]: Encoder: Decoder]
: Encoder[MyBaseListSummary[T]] = deriveEncoder
implicit def decoder[T <: MyBase[T]: Encoder: Decoder]
: Decoder[MyBaseListSummary[T]] = deriveDecoder
}
object MainObject extends App {
val simpleList = MyBaseList[SimpleBase]("simpleId",
Map("one" -> SimpleBase("baseid", SimpleBaseIssue("the mistake"), SimpleBaseSummary("very concise"))),
MyBaseListIssues[SimpleBase]("listIssue", Set(SimpleBaseIssue("a disaster"))),
MyBaseListSummary[SimpleBase]( Map("simplebaseid" -> SimpleBaseSummary("super concise")))
)
val simpleJson = simpleList.asJson
println(simpleJson)
val convertedSimpleList = simpleJson.as[MyBaseList[SimpleBase]]
println(convertedSimpleList)
val sphisticatedList = MyBaseList[SophisticatedBase]("sophisticatedId",
Map("one" -> SophisticatedBase("baseid", "further detail", SophisticatedBaseIssue("the mistake"), SophisticatedBaseSummary("very concise"))),
MyBaseListIssues[SophisticatedBase]("listIssue", Set(SophisticatedBaseIssue("a disaster"))),
MyBaseListSummary[SophisticatedBase]( Map("sophisticatedbaseid" -> SophisticatedBaseSummary("super concise")))
)
val sophisticatedJson = sphisticatedList.asJson
println(sophisticatedJson)
val convertedSophisticatedListDecoder: Result[MyBaseList[SophisticatedBase]] = sophisticatedJson.as[MyBaseList[SophisticatedBase]]
println(convertedSophisticatedListDecoder)
convertedSophisticatedListDecoder match {
case Left(failure) => println(failure)
case Right(list) => println(list)
}
}
It is still essential to make sure every case class inheriting a particular trait has a different number of fields from the others inheriting the same trait, or it at least differs with the other case classes in the naming of those fields.
Otherwise, the decoder would not do its job correctly and decode the json as the first case class matching it in the decoder function, instead of the correct intended one.

scala play json reads for a seal trait or enum type object

I have the following scala code with sealed traits and case objects.
sealed trait StudentType {
val studentLevel: String
val code: Int
}
case object UnderGradFull extends StudentType {
val studentLevel = "UGF"
val code = 11
}
case object UnderGradPart extends StudentType {
val studentLevel = "UGP"
val code = 12
}
case object Grad extends StudentType {
val studentLevel = "GR"
val code = 22
}
case object OtherStudentType extends StudentType {
val studentLevel = "OST"
val code = 20
}
and case class object that makes use of StudentType
case class StudentInfo(studentName: String, studentType: StudentType)
object StudentInfo {
implicit val reads: Reads[StudentInfo] = (
(JsPath \ "studentName").read[String] and
(JsPath \ "studentType").read[StudentType]
)(StudentInfo.apply _)
implicit val writes: Writes[StudentInfo] = (
(JsPath \ "studentName").write[String] and
(JsPath \ "studentType").write[StudentType]
)(unlift(StudentInfo.unapply))
}
how can I create implicit reads/writes for the StudentType trait?
play-json seems to have out-of-the-box support for sealed traits. Analysing
pull request that introduced the feature
other pull requests around sealed traits
tests that show usage
docs
it seems the way to use it is as follows
sealed trait Family
case class ChildA(status: Boolean) extends Family
case class ChildB(name: String, age: Int) extends Family
implicit val childAFormat = Json.format[ChildA]
implicit val childBFormat = Json.format[ChildB]
implicit val familyFormat = Json.format[Family]
println(
Json.parse(
"""{
| "name": "Bob",
| "age": 10,
| "_type": "controllers.ChildB"
|}""".stripMargin
).as[Family]
)
which prints out ChildB(Bob,10).
play-json-extensions also seems to support sealed traits like so
sealed trait Family
case class ChildA(status: Boolean) extends Family
case class ChildB(name: String, age: Int) extends Family
implicit val childBFormat = Jsonx.formatCaseClass[ChildB]
implicit val childAFormat = Jsonx.formatCaseClass[ChildA]
implicit val familyFormat = Jsonx.formatSealed[Family]
println(
Json.parse(
"""{
| "name": "Bob",
| "age": 10
|}""".stripMargin
).as[Family]
)
which prints out ChildB(Bob,10).
Regarding case objects I was only able to get it working when they are empty
sealed trait Family
case object ChildA extends Family
case object ChildB extends Family
implicit val childAFormat = Json.format[ChildA.type]
implicit val childBFormat = Json.format[ChildB.type]
implicit val familyFormat = Json.format[Family]
so I am not sure how to solve your question exactly, however hopefully this answer gives some direction.

Play json absent-sensitive Reads

I'd like to have a reads like Reads[Patch[T]] for a case class like this
sealed trait Patch[+T]
case class Update[+T](value: T) extends Patch[T]
case object Delete extends Patch[Nothing]
case object Ignore extends Patch[Nothing]
where a missing json value reads to Ignore, a null json value reads to Delete and a valid present value reads to Patch.
Is it possible to implement a Reads like this?
Json4s has a JNothing type, does play json have some way to achieve the same functionality (I know there is no nothing type under JsValue)?
Edit: for context on how this might be used see the json merge patch rfc.
Leaving aside discussions of whether Patch[Nothing] is a good idea, if we use this family of objects:
sealed trait Patch[+T]
case class Update[+T](value: T) extends Patch[T]
case object Delete extends Patch[Nothing]
case object Ignore extends Patch[Nothing]
We can get the desired behaviour by implementing a wrapper class:
case class PatchContainer[T](patch: Patch[T])
We have to do this as otherwise we lose the all-important distinction between a null value and a completely missing patch.
Now we can write a Reads for a PatchContainer[T] as long as we supply a suitable Reads[T] (e.g. for a String or Int etc):
class PatchContainerJson[T](implicit val rdst:Reads[T]) {
implicit val patchContainerReads = new Reads[PatchContainer[T]] {
override def reads(json: JsValue): JsResult[PatchContainer[T]] = {
json.validate[JsObject].map { obj =>
(obj \ "patch").asOpt[T].fold[PatchContainer[T]] {
if (obj.keys.contains("patch")) {
PatchContainer(Delete)
} else {
PatchContainer(Ignore)
}
} { v =>
PatchContainer(Update(v))
}
}
}
}
}
The "trick" here is detecting whether there is a patch key in the object (using keys.contains), to get the desired Delete vs Ignore behaviour.
Examples of usage:
scala> import play.api.libs.json._
scala> val json = Json.parse(""" { "patch": 42 } """ )
json: play.api.libs.json.JsValue = {"patch":42}
scala> val pcti = new PatchContainerJson[Int]()
scala> import pcti._
scala> val result = json.validate[PatchContainer[Int]]
result: play.api.libs.json.JsResult[models.PatchContainer[Int]] = JsSuccess(PatchContainer(Update(42)),)
scala> result.get.patch
res0: models.Patch[Int] = Update(42)
and
...
scala> val ignoredJson = Json.parse(""" { } """)
scala> ignoredJson.validate[PatchContainer[Int]]
res1: play.api.libs.json.JsResult[models.PatchContainer[Int]] = JsSuccess(PatchContainer(Ignore),)
and
scala> val deleteJson = Json.parse(""" { "patch": null } """)
scala> deleteJson.validate[PatchContainer[Int]]
res2: play.api.libs.json.JsResult[models.PatchContainer[Int]] = JsSuccess(PatchContainer(Delete),)

Play 2.1 Json serialization for traits?

I have this:
package models
import play.api.libs.json._
import play.api.libs.functional.syntax._
object ModelWrites {
implicit val tmoWrites= Json.writes[TestModelObject]
implicit val ihWrites = Json.writes[IntHolder]
}
case class TestModelObject(s1:String, s2:String)
case class IntHolder(i1:Int, i2:Int)
trait HasInts {
val ints: List[IntHolder]
}
When I do this:
scala> val tmo = new TestModelObject("hello", "world") with HasInts {
val ints = List(IntHolder(1,2), IntHolder(3,4))
}
scala> Json.toJson(tmo)
res0: play.api.libs.json.JsValue = {"s1":"hello","s2":"world"}
how can I implicity serialize the mixed-in val 'ints'? Like:
scala> val someInts = List(IntHolder(8,9), IntHolder(10,11))
someInts: List[models.IntHolder] = List(IntHolder(8,9), IntHolder(10,11))
scala> Json.toJson(someInts)
res1: play.api.libs.json.JsValue = [{"i1":8,"i2":9},{"i1":10,"i2":11}]
Note: if I try: implicit val hasIntsWrites = Json.writes[HasInts] I (expectedly?) get:
[error] Models.scala:10: No unapply function found
[error] implicit val hasIntsWrites = Json.writes[HasInts]
[error] ^
You're not going to be able to use the experimental "Inception" feature (Json.writes[...]) directly here, since that only works on case classes. You can, however, build on the Writes instances that Inception can provide to accomplish what you want with only a very little boilerplate.
Note that I'm ignoring the question of whether mixing in a trait when instantiating a case class like this is a good idea—it probably isn't—but the approach I give here will work in the more general case as well.
First for the classes and imports (no changes here):
case class TestModelObject(s1: String, s2: String)
case class IntHolder(i1: Int, i2: Int)
trait HasInts { val ints: List[IntHolder] }
import play.api.libs.json._
import play.api.libs.functional.syntax._
Now we need to put all our lower-priority instances into a trait to make sure that the compiler will pick the right one, since TestModelObject with HasInts is a subtype of both TestModelObject and HasInts:
trait LowPriorityWritesInstances {
implicit val tmoWrites = Json.writes[TestModelObject]
implicit val ihWrites = Json.writes[IntHolder]
implicit object hiWrites extends OWrites[HasInts] {
def writes(hi: HasInts) = Json.obj("ints" -> hi.ints)
}
}
And now the main event:
object WritesInstances extends LowPriorityWritesInstances {
implicit val tmowhiWrites = new Writes[TestModelObject with HasInts] {
def writes(o: TestModelObject with HasInts) =
tmoWrites.writes(o) ++ implicitly[OWrites[HasInts]].writes(o)
}
}
And we're done:
scala> import WritesInstances._
import WritesInstances._
scala> val tmo = new TestModelObject("hello", "world") with HasInts {
| val ints = List(IntHolder(1, 2), IntHolder(3, 4))
| }
scala> println(Json.toJson(tmo))
{"s1":"hello","s2":"world","ints":[{"i1":1,"i2":2},{"i1":3,"i2":4}]}
As desired.

Vector deserialization by using lift-json

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