class 层次结构的 Spray-json 格式,子class 引用基础 class

Spray-json formats for class hierarchy with subclass that references base class

我有一个代表过滤器类型的 class 层次结构,其中一个类型包含一个基本类型列表。我不知道如何为这些类型设置 spray-json 格式,因为基本类型和包含类型的格式化程序需要相互引用。

让我们从 class 层次结构和 json 格式开始,其中有问题的部分被注释掉了:

object Filters {
  sealed trait Filter
  case class SimpleFilter(foo: String) extends Filter
  case class DoubleFilter(foo: String, bar: String) extends Filter
  implicit val simpleFormat = jsonFormat1(SimpleFilter)
  implicit val doubleFormat = jsonFormat2(DoubleFilter)

//  case class AndFilter(filters: List[Filter]) extends Filter
//  implicit val andFormat = lazyFormat(jsonFormat1(AndFilter))    

  // (would really use a type field, keeping simple for example)
  implicit val filterFormat = new RootJsonFormat[Filter] {
    override def write(obj: Filter): JsValue = obj match {
      case x: SimpleFilter => x.toJson
      case x: DoubleFilter => x.toJson
//    case x: AndFilter => x.toJson
    }

    override def read(json: JsValue): Filter = json.asJsObject.getFields("bar") match {
      case Seq(_) => json.convertTo[DoubleFilter]
      case Seq()  => json.convertTo[SimpleFilter]
    }
  }
}

这可以按预期编译和工作,我可以将具体的过滤器子 class 序列化和反序列化为过滤器没问题。

但让我们在 AndFilter 内容中发表评论。现在有麻烦了!在 filterFormat 之前声明 andFormat(如上所述),它不会编译,因为 andFormat 需要 filterFormat:

Error:(17, 43) could not find implicit value for evidence parameter of type spray.json.DefaultJsonProtocol.JF[List[classpath.Filters.Filter]] implicit val andFormat = jsonFormat1(AndFilter)

filterFormat 之后将顺序切换到 andFormat 将使事情编译。但当然我也想将 andFormat-referencing 子句添加到 filter 格式,即写入方法中的 case x: AndFilter => x.toJson 以及读取方法中包含 json.convertTo[AndFilter] 的任何内容。那也不编译:

Error:(23, 34) Cannot find JsonWriter or JsonFormat type class for classpath.Filters.Filter with classpath.Filters.AndFilter case x: AndFilter => x.toJson

我找不到解决这个问题的方法。我试过 spray-json 的 lazyFormat 但它没有帮助(只适用于递归自引用,而不是像这样的交叉引用)。有什么想法吗?

有时您需要稍微帮助一下编译器。在 filterFormat 上添加显式类型和显式 write 将使其编译。

sealed trait Filter
case class SimpleFilter(foo: String) extends Filter
case class DoubleFilter(foo: String, bar: String) extends Filter
case class AndFilter(filters: List[Filter])
implicit val simpleFormat = jsonFormat1(SimpleFilter)
implicit val doubleFormat = jsonFormat2(DoubleFilter)
implicit val andFormat    = jsonFormat1(AndFilter)

implicit val filterFormat: RootJsonFormat[Filter] = new RootJsonFormat[Filter] {
  override def write(obj: Filter): JsValue = obj match {
    case x: SimpleFilter => x.toJson
    case x: DoubleFilter => x.toJson
    case x: AndFilter    => andFormat.write(x)
  }

  override def read(json: JsValue): Filter = json.asJsObject.getFields("bar") match {
    case Seq(_) => json.convertTo[DoubleFilter]
    case Seq()  => json.convertTo[SimpleFilter]
  }
}

我相信以下修改解决了问题中所述的隐式解析 JsonFormat[AndFilter] 的问题。

implicit val andFormat: JsonFormat[AndFilter] = lazyFormat(jsonFormat1(AndFilter))

请注意,我们需要为 andFormat 提供显式类型注释(即 JsonFormat[AddFilter]),以便 SparyJsonSupport 可以将其作为 [=16= 的实例],而不是 lazyFormat:

返回的 JsonFormat
  import spray.json._
  import DefaultJsonProtocol._

  object Filters {
    sealed trait Filter
    case class SimpleFilter(foo: String) extends Filter
    case class DoubleFilter(foo: String, bar: String) extends Filter
    case class AndFilter(filters: List[Filter]) extends Filter

    implicit val simpleFormat = jsonFormat1(SimpleFilter)
    implicit val doubleFormat = jsonFormat2(DoubleFilter)
    implicit val andFormat: JsonFormat[AndFilter] = lazyFormat(jsonFormat1(AndFilter))

    implicit val filterFormat = new RootJsonFormat[Filter] {
      override def write(obj: Filter): JsValue = ???
      override def read(json: JsValue): Filter = ???
    }
  }

详情请见https://github.com/spray/spray-json#jsonformats-for-recursive-types