Using `Composition` with Case Classes for JSON De-serialization

93 views
Skip to first unread message

Kevin Meredith

unread,
Aug 24, 2015, 10:23:31 PM8/24/15
to spray.io User List
Given the following case classes:

scala> case class Foo(x: Int, y: Int)

defined class Foo


scala> case class Parent1(a: String, foo: Foo)

defined class Parent1


I defined two implicit `RootJsonFormat`'s for each type.


scala> object FooFormat extends DefaultJsonProtocol {

     |   implicit val format: RootJsonFormat[Foo] = jsonFormat2(Foo.apply)

     | }

defined object FooFormat


scala> object Parent1Format extends DefaultJsonProtocol {

     |   import FooFormat.format

     |    implicit val format2 = jsonFormat2(Parent1.apply)

     | }

defined object Parent1Format


However, when I tried to de-serialize a JSON object into `Parent1`, I get a compile-time error.


The following works, however:


scala> """ {"a": "foobar", "foo": { "x": 5, "y": 100 } } """.parseJson.convertTo[Parent1]

res2: Parent1 = Parent1(foobar,Foo(5,100))


But, how can I use the key-value pairs of `Foo` for de-serialization - where `foo` is not an expected key?

Kevin Meredith

unread,
Aug 24, 2015, 10:26:13 PM8/24/15
to spray.io User List
I forgot to include the run-time error (not compile-time error as I said) when attempting de-serialization:

scala> """ {"a": "foobar", "x": 5, "y": 100 } """.parseJson.convertTo[Parent1]

spray.json.DeserializationException: Object is missing required member 'foo'

Richard Bradley

unread,
Aug 25, 2015, 11:17:00 AM8/25/15
to spray.io User List
> how can I use the key-value pairs of `Foo` for de-serialization - where `foo` is not an expected key?

There is no support in spray-json for creating a JSON format from the union of the properties of two case classes.
You'll have to write a custom format class to implement this.

Sam Halliday

unread,
Aug 25, 2015, 12:10:18 PM8/25/15
to spray.io User List

Ethan Eldridge

unread,
Aug 25, 2015, 11:43:21 PM8/25/15
to spray.io User List
You can write a custom serialize/deserializer for this.

package example

import spray.json._

case class Foo(x: Int, y: Int)

case class Parent1(a: String, foo: Foo)

object FooFormat extends DefaultJsonProtocol {
 implicit val format: RootJsonFormat[Foo] = jsonFormat2(Foo)
}

object Parent1Format extends DefaultJsonProtocol {
 import FooFormat.format
 implicit val format2 = jsonFormat2(Parent1)
}

class CustomJsonFormat extends RootJsonFormat[Parent1] {
def read(value: JsValue) = value.asJsObject.getFields("a","x", "y") match {
     case Seq(JsString(status), JsNumber(x), JsNumber(y)) =>
       Parent1(status, Foo(x.toInt, y.toInt))
     case _ => deserializationError("a,x,y expected (in that order)")
   }
def write(obj: Parent1) = JsObject(
"a" -> JsString(obj.a), "x" -> JsNumber(obj.foo.x), "y" -> JsNumber(obj.foo.y)
)
}


class Example {
implicit val custom = new CustomJsonFormat()
//""" {"a": "foobar", "foo": { "x": 5, "y": 100 } } """.parseJson.convertTo[Parent1]
val x = """ {"a": "foobar", "x": 5, "y": 100 } """.parseJson.convertTo[Parent1]
println(x)
}


Run that in the console and just make a new Example and you should see something like this:
scala> import example
     | ._
import example._

scala> new Example()
Parent1(foobar,Foo(5,100))
res0: example.Example = example.Example@f77b317

Reply all
Reply to author
Forward
0 new messages