Play Json:定义一个解析JSON数组并忽略某些元素的Reads[T]

Play Json: defining a Reads[T] that parses a JSON array and ignores certain elements

我有一个抽象类型 PolyType 和这种类型的具体实现,TypeATypeBTypeC。目标是解析包含这三种类型的序列化表示的 JSON 数组。每个数组可以同时包含所有三种类型。

我正在使用以下 Reads 进行反序列化:

implicit val polyReads = new Reads[PolyType] {
    override def reads(json: JsValue): JsResult[PolyType] =
      json.validate(
        typeAReads.map(_.asInstanceOf[TypeA]) orElse
        typeBReads.map(_.asInstanceOf[TypeB]) orElse
        typeCReads.map(_.asInstanceOf[TypeC])
      )
}

最终,我可以像这样使用它来解析包含以下三种类型的 JSON 数组:

val json: JsValue = { ... }
val result = json.validate[Seq[PolyType]] 

// => JsResult[Seq[PolyType]], Seq contains instances of A, B and C

这在概念上可行。结果是 Seq[PolyType],其中包含 TypeATypeBTypeC 的实例。请注意,我不必手动处理 Seq,例如我不需要明确检查输入是否表示 json 数组(如果不是,我得到一个 JsError,这很好)。


但我真正想做的是:

首先,只提取TypeA个实例。这是因为 TypeA 包含正确解析 TypeBTypeC 所需的定义。因此 TypeBTypeCReads 是 "dynamic" 并且需要 TypeA 的具体实例。我没有为 B 和 C 定义那些 "dynamic" Reads 的问题。

但我想知道:

我不知道TypeA是如何提取TypeBTypeC的,所以我只能告诉你如何提取TypeA的所有实例。首先,一些示例类型:

进口play.api.libs.json._

trait PolyType

case class TypeA(a: String) extends PolyType

case class TypeB(b: String) extends PolyType

case class TypeC(c: String) extends PolyType

implicit val typeAReads: Reads[TypeA] = Json.reads[TypeA]
implicit val typeBReads: Reads[TypeB] = Json.reads[TypeB]
implicit val typeCReads: Reads[TypeC] = Json.reads[TypeC]

示例数据:

val json = Json.parse("""
    [
        {"a": "Some A"},
        {"b": "Some B"},
        {"c": "Some C"},
        {"c": "Other C"},
        {"a": "Other A"},
        {"b": "Other B"}
    ]
""")

然后,我定义了一个 Reads[Seq[TypeA]],它将首先尝试 validate JSON 作为 JsArray,然后分别 validate 数组的每个成员作为 TypeA,并且 collect 只有那些成功的人。

implicit val typeASeqReads = new Reads[Seq[TypeA]] {
    def reads(js: JsValue): JsResult[Seq[TypeA]] = {
        js.validate[JsArray].map { array =>
            array.value.map(_.validate[TypeA])
                .collect { case JsSuccess(a, _) => a }
        }
    }
}

结果:

scala> json.validate[Seq[TypeA]]
res0: play.api.libs.json.JsResult[Seq[TypeA]] = JsSuccess(ListBuffer(TypeA(Some A), TypeA(Other A)),)

注意事项:

  • 这将 始终 return JsSuccess 除非 JSON 不是数组。因此,如果没有有效的 TypeA,它将 return 一个空的 Seq
  • 很难区分无效的 TypeA 和其他类型。

最后,您需要一个特殊的 Reads[Seq[PolyType]] 来处理多次遍历列表的要求。