用于类层次结构的Spray-json格式,其子类引用基类

时间:2016-03-03 19:48:05

标签: scala spray-json

我有一个表示过滤器类型的类层次结构,其中一个类型包含基类型列表。我无法弄清楚如何为这些类型设置spray-json格式,因为基类型和包含类型的格式化程序需要相互引用。

让我们从类层次结构和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]
    }
  }
}

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

但是让我们在AndFilter的评论中发表评论。现在有麻烦了!如果andFormatfilterFormat之前声明(如上所述),则无法编译,因为andFormat需要filterFormat

  

错误:(17,43)找不到类型为spray.json.DefaultJsonProtocol.JF [List [classpath.Filters.Filter]]的证据参数的隐含值         隐式val和Format = jsonFormat1(AndFilter)

andFormat之后将订单切换到filterFormat将使事情编译。但当然我还想添加andFormat - 引用filter格式的子句,即write方法中的case x: AndFilter => x.toJson以及read方法中包含json.convertTo[AndFilter]的内容。而且这不会编译:

  

错误:(23,34)找不到带有classpath.Filters.AndFilter的classpath.Filters.Filter的JsonWriter或JsonFormat类型类             case x:AndFilter => x.toJson

我无法找到解决方法。我已经尝试了喷射json的lazyFormat,但它没有帮助(仅适用于递归自引用,不适用于这样的交叉引用)。有什么想法吗?

2 个答案:

答案 0 :(得分:1)

有时您需要帮助编译器一点。在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]
  }
}

答案 1 :(得分:1)

我认为以下修改解决了问题中所述的JsonFormat[AndFilter]隐式解决方案的问题。

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

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

  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