spray-json: 序列化一个通用特征
spray-json: Serialize a generic trait
spray-json 在 T
的实例上调用 toJson
时依赖于范围内隐式 JsonWriter[T]
的存在。
假设我有一个具有多个具体子类型的特征,每个子类型都有一个 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
无法编译,因为没有适用于 Base 的 JsonWriter,即使 有 所有具体子类型的编写器。
如何重新组织此代码,以便 Base
的通用函数使用适当的 json 格式化程序?
编组 Base
需要 JsonFormat
。以下是解决所述问题的方法之一。
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} ")
}
}
}
}
因此,BaseJsonProtocol
可用于编组 Base
的实例,如下所示。
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))
希望对您有所帮助!
我意识到一个解决方案是使 go
成为具有类型绑定的泛型函数,并声明隐式值..."explicitly"
def go[T <: Base](t: T)(implicit fmt: JsonWriter[T]) =
t.toJson
现在函数可以编译了,因为 JsonWriter
被承诺为函数参数,并且每个调用站点都可以根据上下文引入 JsonWriter[T]
的具体实现(FooFormat
或 BarFormat
).
您可以使用具有类型和上下文边界的泛型方法。像这样:
def go[T <: Base : JsonWriter](t: T) = t.toJson
spray-json 在 T
的实例上调用 toJson
时依赖于范围内隐式 JsonWriter[T]
的存在。
假设我有一个具有多个具体子类型的特征,每个子类型都有一个 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
无法编译,因为没有适用于 Base 的 JsonWriter,即使 有 所有具体子类型的编写器。
如何重新组织此代码,以便 Base
的通用函数使用适当的 json 格式化程序?
编组 Base
需要 JsonFormat
。以下是解决所述问题的方法之一。
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} ")
}
}
}
}
因此,BaseJsonProtocol
可用于编组 Base
的实例,如下所示。
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))
希望对您有所帮助!
我意识到一个解决方案是使 go
成为具有类型绑定的泛型函数,并声明隐式值..."explicitly"
def go[T <: Base](t: T)(implicit fmt: JsonWriter[T]) =
t.toJson
现在函数可以编译了,因为 JsonWriter
被承诺为函数参数,并且每个调用站点都可以根据上下文引入 JsonWriter[T]
的具体实现(FooFormat
或 BarFormat
).
您可以使用具有类型和上下文边界的泛型方法。像这样:
def go[T <: Base : JsonWriter](t: T) = t.toJson