【问题标题】:JsonFormat for abstract class with generic parameter具有通用参数的抽象类的 JsonFormat
【发布时间】:2018-04-08 18:07:17
【问题描述】:

我正在尝试为一个抽象类编写一个 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 设置为任何适用的案例类。

【问题讨论】:

  • animalFormat(t: T)参数有必要吗?如果删除它会怎样?
  • 如果我删除它,我仍然会收到同样的错误...

标签: scala generics spray-json


【解决方案1】:

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

【讨论】:

    【解决方案2】:

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

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

    您对animalFormat 的定义看起来错误/奇怪,原因如下:

    • 类型很奇怪,T &lt;: Animal[T] 与您的类型不对应,即您没有 CatData &lt;: 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 中没有类型信息。

    【讨论】:

      猜你喜欢
      • 2016-06-30
      • 2011-09-08
      • 1970-01-01
      • 2017-08-04
      • 2011-06-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-11-13
      相关资源
      最近更新 更多