使用 Play 解析命名 JSON 对象的列表

Parsing a list of named JSON objects with Play

我有一个来自外部 API 的 JSON,我无法控制它包含未定义数量的命名子对象,如下所示:

{
  // ...
  "promotions": {
    "5": {
      "id": 5,
      "name": "Promo",
      "translations": {
        "fr": "Promo2",
        "de": "Promo2",
        // ...
      }
    },
    "6": {
      "id": 6,
      "name": "Promo2",
      "translations": {
        "fr": "Promo2",
        "de": "Promo2",
        // ...
      }
    },
    // ...
  }
}    

我希望使用 Play JSON 库(使用 Reads 组合器)将 promotions 的内容转换为 Promotion 对象的列表,但我不知道如何处理这个。

听起来您需要编写 Reads[Seq[Promotion]] 的自定义实例。您可以使用 reader 宏来创建 Reads[Promotion],然后在手动遍历 json 树并遍历促销编号的自定义实现中使用它。

如果在 json 密钥中不需要 ID,您可以使用 values: Iterable[JsValue]JsObject。所以:

  1. 要使用数组转换为 Json,您可以使用转换器

    (__ \ "promotions").json.update(
      __.read[JsObject].map(_.values.toList).map(JsArray)
    )
    

    结果将是:

    scala> res28: play.api.libs.json.JsResult[play.api.libs.json.JsObject] = JsSuccess({"promotions":[{"id":5,"name":"Promo","translations":{"fr":"Promo2","de":"Promo2"}},{"id":6,"name":"Promo2","translations":{"fr":"Promo2","de":"Promo2"}}]},/promotions)
    
  2. 对于List[Promotion](你必须有隐式Promotionreader,例如使用宏implicit val PromotionRead = Json.reads[Promotion]或者你可以使用自己的显式reader 作为 as[Promotion] 的参数),您的 reader 将是:

    (__ \ "promotions").read[JsObject].map(_.values.toList.map(_.as[Promotion]))
    

    结果(case class Promotion(id: Long, name: String)):

    scala> res40: play.api.libs.json.JsResult[List[Promotion]] = JsSuccess(List(Promotion(5,Promo), Promotion(6,Promo2)),/promotions)