Scala Play 阅读:How to Flatten Json containing Array of arrays to model

Scala Play Read: How to Flatten Json containing Array of arrays to model

我正在寻找一种定义 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 需要另一个读取。我如何将扁平化的列表包装到另一个读取中? 或者有更简单的方法吗?

我将给出 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 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