Scala Play Read:如何Flatten Json包含数组的数组模型



我正在寻找一种方法来定义一个Reads,它允许我映射包含以下结构的JSON:

{
"offers": [
[
{
"id": "1234",
(etc.)
}
]
]
}

模拟这样的case class TransportOffer(offers: List[Offer])

不幸的是,我还没能做到这一点。这是我的代码:

implicit val transportOfferReads: Reads[TransportOffer] = (
(JsPath  "offers").read[List[List[Offer]]].flatMap(r => r.flatten)
)(TransportOffer.apply _)

在这种情况下,平坦化是不可能的,因为flatMap需要另一个read。如何将扁平的List包装成另一个Reads?或者有更简单的方法吗?

我将呈现3个选项:

  1. 在短时间内变平:
case class Offer(id: String)
object Offer {
implicit val format: OFormat[Offer] = Json.format[Offer]
}
case class TransportOffer(offers: List[Offer])
object TransportOffer {
implicit val transportOfferReads: Reads[TransportOffer] =
(JsPath  "offers").read[List[List[Offer]]].map(x => TransportOffer(x.flatten))
}

然后调用:

Json.parse(jsonString).validate[TransportOffer].foreach(println)

输出:

TransportOffer(List(Offer(1234)))

在Scastie运行的代码

  1. 显式写入Reads:
implicit val transportOfferReads: Reads[TransportOffer] = (json: JsValue) => {
json  "offers" match {
case JsUndefined() =>
JsError("offers undefined")
case JsDefined(value) =>
value.validate[List[List[Offer]]].map(x => TransportOffer(x.flatten))
}

在Scastie上运行的代码。

  1. 首先将json转换为您想要的模型。为此,定义transformer:
val jsonTransformer = (__  "offers").json
.update(__.read[JsArray].map{o => {
JsArray(o.value.flatMap(_.asOpt[JsArray].map(_.value)).flatten)
}})
然后,假设我们有case类和它们的格式化器:
case class Offer(id: String)
object Offer {
implicit val format: OFormat[Offer] = Json.format[Offer]
}
case class TransportOffer(offers: List[Offer])
object TransportOffer {
implicit val format: OFormat[TransportOffer] = Json.format[TransportOffer]
}

可以调用:

Json.parse(jsonString).transform(jsonTransformer) match {
case JsSuccess(value, _) =>
value.validate[TransportOffer].foreach(println)
case JsError(errors) =>
println(errors)
???
}

输出是:

TransportOffer(List(Offer(1234)))

在Scastie上运行的代码。

最新更新