带有泛型参数

时间:2017-10-27 07:41:08

标签: scala generics spray-json

我正在尝试为一个抽象类编写一个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。

2 个答案:

答案 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内的子类上的模式匹配。