JsonFormat for abstract class with generic parameter

JsonFormat for abstract class with generic parameter

我正在尝试为抽象 class 编写一个 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 中的字段编写 json 并将 data 设置为 class 适用的任何情况。

我不使用 spray-json(我对 play-json 有更多的经验),但我会尝试通过指出一些奇怪的事情来提供帮助你的代码。

我不确定您是否需要 implicit val catFormat = jsonFormat1(Cat),除非您希望在已知类型为 Cat.

时应用它而不是 animalFormat

您对 animalFormat 的定义看起来 wrong/strange,原因如下:

  • 类型很奇怪,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.

中没有类型信息

您需要为 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 中子类的模式匹配。