spray-json relies on the presence of an in-scope, implicit JsonWriter[T]
when calling toJson
on an instance of T
.
Say I have a trait with several concrete subtypes, each of which has a JsonWriter:
trait Base
case class Foo(a: Int) extends Base
case class Bar(a: Int, b: Int) extends Base
implicit val FooFormat = jsonFormat1(Foo)
implicit val BarFormat = jsonFormat2(Bar)
def go(o: Base) = {
o.toJson
}
go
doesn't compile because there's no JsonWriter for Base, even though there are writers for all of the concrete subtypes.
How can I reorganize this code such that generic functions of Base
use the appropriate json formatters?
You can use a generic method with type and context bounds. Like this:
def go[T <: Base : JsonWriter](t: T) = t.toJson
Marshalling Base
would require a JsonFormat
. Following is one of the ways to approach the stated issue.
import spray.json._
object BaseJsonProtocol extends DefaultJsonProtocol {
implicit val FooFormat = jsonFormat1(Foo)
implicit val BarFormat = jsonFormat2(Bar)
implicit object BaseJsonFormat extends RootJsonFormat[Base] {
def write(obj: Base) = obj match {
case x: Foo ⇒ x.toJson
case y: Bar ⇒ y.toJson
case unknown @ _ => serializationError(s"Marshalling issue with ${unknown}")
}
def read(value: JsValue) = {
value.asJsObject.getFields("a","b") match {
case Seq(JsNumber(a)) => Foo(a.toInt)
case Seq(JsNumber(a), JsNumber(b)) => Bar(a.toInt, b.toInt)
case unknown @ _ => deserializationError(s"Unmarshalling issue with ${unknown} ")
}
}
}
}
As a result, the BaseJsonProtocol
can be used to marshall an instance of Base
as follows.
import BaseJsonProtocol._
def go(o: Base) = o.toJson
assert (go(Foo(10)).convertTo[Foo] == Foo(10))
assert (go(Foo(10).asInstanceOf[Base]).convertTo[Foo] == Foo(10))
assert (go(Bar(10,100)).convertTo[Bar] == Bar(10, 100))
Hope it helps!
I realized one solution is to make go
a generic function with a type bound, and declare the implicit value..."explicitly"
def go[T <: Base](t: T)(implicit fmt: JsonWriter[T]) =
t.toJson
Now the function can compile because the JsonWriter
is promised as a function parameter, and each call site can pull in a concrete implementation of JsonWriter[T]
depending on the context (FooFormat
or BarFormat
).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With