【问题标题】:Retrieving a particular field inside objects in JsArray检索 JsArray 中对象内的特定字段
【发布时间】:2019-12-15 20:12:41
【问题描述】:

我的 JSON 响应的一部分如下所示:

"resources": [{
        "password": "",
        "metadata": {
            "updated_at": "20190806172149Z",
            "guid": "e1be511a-eb8e-1038-9547-0fff94eeae4b",
            "created_at": "20190405013547Z",
            "url": ""
        },
        "iam": false,
        "email": "<some mail id>",
        "authentication": {
            "method": "internal",
            "policy_id": "Default"
        }
    }, {
        "password": "",
        "metadata": {
            "updated_at": "20190416192020Z",
            "guid": "6b47118c-f4c8-1038-8d93-ed6d7155964a",
            "created_at": "20190416192020Z",
            "url": ""
        },
        "iam": true,
        "email": "<some mail id>",
        "authentication": {
            "method": "internal",
            "policy_id": null
        }
    },
    ...
]

我正在使用 Play 框架提供的 Json 助手来解析这个 Json,如下所示:

val resources: JsArray = response("resources").as[JsArray]

现在我需要从resources JsArray 中的所有这些对象中提取字段email。为此,我尝试编写一个 foreach 循环,例如:

for (resource <- resources) {

}

但我在&lt;- 符号处收到错误Cannot resolve symbol foreach。如何从 JsArray 内的每个 JSON 对象中检索特定字段,例如 email

【问题讨论】:

    标签: scala playframework-2.0 play-json playframework-json


    【解决方案1】:

    使用 播放 JSON 我总是使用 case classes。所以你的例子看起来像:

    import play.api.libs.json._
    
    case class Resource(password: String, metadata: JsObject, iam: Boolean, email: String, authentication: JsObject)
    
    object Resource {
      implicit val jsonFormat: Format[Resource] = Json.format[Resource]
    }
    
    val resources: Seq[Resource] = response("resources").validate[Seq[Resource]] match {
      case JsSuccess(res, _) => res
      case errors => // handle errors , e.g. throw new IllegalArgumentException(..)
    }
    

    现在您可以以type-safe 的方式访问任何字段。

    当然,您可以用相同的方式将JsObjects 替换为case classes - 如果您在我的回答中需要这个,请告诉我。

    但在您的情况下,您只需要email,因此不需要:

    resources.map(_.email) // returns Seq[String]
    

    【讨论】:

    • @Pedro Correia Luís 感谢您的提示 - 我调整了答案
    • 您好,谢谢您的回答。现在我的要求只是获取电子邮件,但这不是整个输出,就像元数据和授权一样,我还有一些其他的东西需要提取。您能否编辑答案以显示案例类?
    • 我不确定如何处理这些错误。我知道如果解析任何字段出现问题,那将是一个错误,但是我该如何处理,因为case errors 的输出还需要Resource 类型的对象
    • @Sparker0i 1. Pedro 的回答确实已经在他的回答中提供了这一点。 2. 更实用的方法是返回Try[Seq[Resource]] 并在稍后处理错误情况。
    【解决方案2】:

    就像@pme 说的你应该使用案例类,它们应该看起来像这样:

    import java.util.UUID
    
    import play.api.libs.json._
    
    
    case class Resource(password:String, metadata: Metadata, iam:Boolean, email:UUID, authentication:Authentication)
    
    object Resource{
      implicit val resourcesImplicit: OFormat[Resource] = Json.format[Resource]
    }
    
    case class Metadata(updatedAt:String, guid:UUID, createdAt:String, url:String)
    
    object Metadata{
      implicit val metadataImplicit: OFormat[Metadata] = Json.format[Metadata]
    }
    
    
    case class Authentication(method:String, policyId: String)
    
    object Authentication{
      implicit val authenticationImplicit: OFormat[Authentication] = 
    Json.format[Authentication]
    }
    

    您也可以使用写入和读取来代替 OFormat,或者自定义写入和读取,我使用 OFormat 是因为它不那么冗长。

    然后,当您收到回复时,您可以验证它们,您可以按照@pme 所说的方式验证它们,或者按照我的方式验证它们:

    val response_ = response("resources").validate[Seq[Resource]]
    response_.fold(
        errors => Future.succeful(BadRequest(JsError.toJson(errors)),
    resources => resources.map(_.email))// extracting emails from your objects
        ???
    )
    

    所以这里你在 Json 无效时做一些事情,当 Json 有效时做另一件事,行为和 pme 做的一样,只是在我看来更优雅一点

    【讨论】:

    • 如果目标是为每个数组元素获取单个值 email: String(以 Seq[String] 结尾),则无需添加案例类(“只是”从宏生成Reads)
    • 然后呢?电子邮件不是结构类型(Value 类而不是 Case 类)
    • @pedro,我尝试了response 部分的代码,但在BadRequest 部分出现错误。
    • Here。我正在使用最新的 Play 框架。也许这就是为什么..?
    • 我不认为是这种情况,可能是缺少导入。如果您使用 BadRequest,您仍然有错误吗?也拿???,只是因为我不知道收到电子邮件后你还想做什么
    【解决方案3】:

    假设你的 json 看起来像这样:

    val jsonString =
      """
        |{
        |  "resources": [
        |    {
        |      "password": "",
        |      "metadata": {
        |        "updated_at": "20190806172149Z",
        |        "guid": "e1be511a-eb8e-1038-9547-0fff94eeae4b",
        |        "created_at": "20190405013547Z",
        |        "url": ""
        |      },
        |      "iam": false,
        |      "email": "<some mail id1>",
        |      "authentication": {
        |        "method": "internal",
        |        "policy_id": "Default"
        |      }
        |    },
        |    {
        |      "password": "",
        |      "metadata": {
        |        "updated_at": "20190416192020Z",
        |        "guid": "6b47118c-f4c8-1038-8d93-ed6d7155964a",
        |        "created_at": "20190416192020Z",
        |        "url": ""
        |      },
        |      "iam": true,
        |      "email": "<some mail id2>",
        |      "authentication": {
        |        "method": "internal",
        |        "policy_id": null
        |      }
        |    }
        |  ]
        |}
      """.stripMargin
    

    你可以这样做:

    (Json.parse(jsonString) \ "resources").as[JsValue] match{
      case js: JsArray => js.value.foreach(x => println((x \ "email").as[String]))
      case x => println((x \ "email").as[String])
    }
    

    或:

    (Json.parse(jsonString) \ "resources").validate[JsArray] match {
      case s: JsSuccess[JsArray] => s.get.value.foreach(x => println((x \ "email").as[String]))
      case _: JsError => arr().value //or do something else
    }
    

    两者都适合我。

    【讨论】:

      【解决方案4】:

      resources 是一个JsArray,一个不提供.flatMap 的类型,因此不能在&lt;- 的右侧用于理解。

      val emailReads: Reads[String] = (JsPath \ "email").reads[String]
      val resourcesReads = Reads.seqReads(emailReads)
      
      val r: JsResult[Seq[String]] = resources.validate(resources reads)
      

      【讨论】:

      • 这不是我想要的
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-05-09
      • 2015-08-24
      • 1970-01-01
      • 2020-04-01
      • 2021-11-20
      • 1970-01-01
      • 2010-09-20
      相关资源
      最近更新 更多