我有一个表示过滤器类型的类层次结构,其中一个类型包含基类型列表。我无法弄清楚如何为这些类型设置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
的评论中发表评论。现在有麻烦了!如果andFormat
在filterFormat
之前声明(如上所述),则无法编译,因为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
,但它没有帮助(仅适用于递归自引用,不适用于这样的交叉引用)。有什么想法吗?
答案 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。