我正在尝试为一个抽象类编写一个JsonFormat,其泛型参数看起来像这样:
abstract class Animal[A] {
def data: A
def otherStuff: String = "stuff"
}
case class CatData(catField: String)
case class Cat(data: CatData) extends Animal[CatData]
到目前为止,我的尝试似乎是:
object AnimalProtocol extends DefaultJsonProtocol {
implicit val catDataFormat = jsonFormat1(CatData)
implicit val catFormat = jsonFormat1(Cat)
implicit def animalFormat[T <: Animal[T]](t: T)(implicit fmt: JsonWriter[T]) = new RootJsonFormat[Animal[T]] {
def write(obj: Animal[T]) = obj match {
case x: Cat => catFormat.write(x)
}
def read(json: JsValue) = ???
}
现在,如果我尝试这样做:
import AnimalProtocol._
val cat: Animal[CatData] = Cat(CatData("this is cat data"))
我收到编译错误:
Cannot find JsonWriter or JsonFormat type class for Animal[CatData]
我怎样才能让它发挥作用?最后,我想用Animal
中的字段和data
设置适用于任何案例类的字段来编写json。
答案 0 :(得分:2)
我没有使用spray-json(我对play-json有更多的经验),但是我会在你的代码中指出一些奇怪的东西来帮助我。 / p>
我不确定您是否需要implicit val catFormat = jsonFormat1(Cat)
,除非您希望在animalFormat
知道类型时将其应用于Cat
。
由于以下原因,animalFormat
的定义看似错误/奇怪:
T <: Animal[T]
与您的类型不对应,即您没有CatData <: Animal[CatData]
t
fmt
(而是在obj
上进行模式匹配)我建议定义一个静态animalFormat
,类似于(不确定通配符类型_
):
val animalFormat: RootJsonFormat[Animal[_]] = new RootJsonFormat[Animal[_]] {
def write(obj: Animal[_]) = {
JsObject(
"otherStuff" -> JsString(obj.otherStuff),
"data" -> obj match {
case x: Cat => catDataFormat.write(x.data)
}
)
def read(json: JsValue) = ???
}
或者,不使用模式匹配:
implicit def animalFormat[T](implicit fmt: JsonWriter[T]) = new RootJsonFormat[Animal[T]] {
def write(obj: Animal[T]) =
JsObject(
"otherStuff" -> JsString(obj.otherStuff),
"data" -> fmt.write(obj.data)
)
def read(json: JsValue) = ???
}
请注意,通过这种方法,您无法读取通用Animal
,因为json中没有类型信息。
答案 1 :(得分:2)
您需要在implicit def
中为Animal的通用字段和子类提供类型参数:
object AnimalProtocol2 extends DefaultJsonProtocol {
implicit val catDataFormat = jsonFormat1(CatData)
implicit def animalFormat[A, T <: Animal[A]](implicit fmt: JsonWriter[A]): RootJsonFormat[T] = new RootJsonFormat[T] {
def write(obj: T) = {
JsObject(
"data" -> obj.data.toJson,
"otherStuff" -> obj.otherStuff.toJson
)
}
def read(json: JsValue) = ???
}
}
这也允许你摆脱animalFormat
内的子类上的模式匹配。