1

我的 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]

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

for (resource <- resources) {

}

但我Cannot resolve symbol foreach<-标志处遇到错误。如何email从每个 JSON 对象中检索特定字段JsArray

4

4 回答 4

2

对于Play 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]
于 2019-08-08T07:33:52.847 回答
0

所以就像@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 所做的相同,只是在我看来更优雅一点

于 2019-08-08T09:07:36.163 回答
0

假设你的 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
}

两者都对我有用。

于 2019-08-09T09:44:20.143 回答
-1

resourcesis a JsArray,不提供的类型不能在for 理解.flatMap的右边使用。<-

val emailReads: Reads[String] = (JsPath \ "email").reads[String]
val resourcesReads = Reads.seqReads(emailReads)

val r: JsResult[Seq[String]] = resources.validate(resources reads)
于 2019-08-08T08:04:42.657 回答