Play Json:定义一个解析JSON数组并忽略某些元素的Reads[T]
Play Json: defining a Reads[T] that parses a JSON array and ignores certain elements
我有一个抽象类型 PolyType
和这种类型的具体实现,TypeA
、TypeB
和 TypeC
。目标是解析包含这三种类型的序列化表示的 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]
,其中包含 TypeA
、TypeB
和 TypeC
的实例。请注意,我不必手动处理 Seq
,例如我不需要明确检查输入是否表示 json 数组(如果不是,我得到一个 JsError
,这很好)。
但我真正想做的是:
首先,只提取TypeA
个实例。这是因为 TypeA
包含正确解析 TypeB
和 TypeC
所需的定义。因此 TypeB
和 TypeC
的 Reads
是 "dynamic" 并且需要 TypeA
的具体实例。我没有为 B 和 C 定义那些 "dynamic" Reads
的问题。
但我想知道:
- 我如何首先从提取
TypeA
和稍后TypeB/C
相同 json 结构?
- 例如只做
val a = json.validate[Seq[TypeA]]
是行不通的,因为 json 数组还包含其他两种会导致错误的类型。
我不知道TypeA
是如何提取TypeB
和TypeC
的,所以我只能告诉你如何提取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]]
来处理多次遍历列表的要求。
我有一个抽象类型 PolyType
和这种类型的具体实现,TypeA
、TypeB
和 TypeC
。目标是解析包含这三种类型的序列化表示的 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]
,其中包含 TypeA
、TypeB
和 TypeC
的实例。请注意,我不必手动处理 Seq
,例如我不需要明确检查输入是否表示 json 数组(如果不是,我得到一个 JsError
,这很好)。
但我真正想做的是:
首先,只提取TypeA
个实例。这是因为 TypeA
包含正确解析 TypeB
和 TypeC
所需的定义。因此 TypeB
和 TypeC
的 Reads
是 "dynamic" 并且需要 TypeA
的具体实例。我没有为 B 和 C 定义那些 "dynamic" Reads
的问题。
但我想知道:
- 我如何首先从提取
TypeA
和稍后TypeB/C
相同 json 结构? - 例如只做
val a = json.validate[Seq[TypeA]]
是行不通的,因为 json 数组还包含其他两种会导致错误的类型。
我不知道TypeA
是如何提取TypeB
和TypeC
的,所以我只能告诉你如何提取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]]
来处理多次遍历列表的要求。