Gatling : 获取 JSON 数组中第一个对象的第一个元素

Gatling : get the first element of the first object in a JSON Array

我有这个 JSON 对象 :

{"isCompany":false,"accommodations":[{"id":"00000000031000000067","isChecked":false,"name":"5 JULI 2017","addressLine1":"STRAAT 10 ","addressLine2":"1000 New York","nightsDeclared":0,"schoolNightsDeclared":0,"schoolNightsAttached":0,"taxableNights":0.0,"totalPayment":0.0,"isInProgress":false,"isLate":false,"isPayed":"false","deadline":"2021-12-31","initialAmount":0.0,"remainingAmount":0.0},{"id":"00000000031000006362","isChecked":false,"name":"BELLEVIE","addressLine1":"STRAAT 10 ","addressLine2":"1000 New York","nightsDeclared":0,"schoolNightsDeclared":0,"schoolNightsAttached":0,"taxableNights":0.0,"totalPayment":0.0,"isInProgress":false,"isLate":false,"isPayed":"false","deadline":"2021-12-31","initialAmount":0.0,"remainingAmount":0.0}]}

如果美化,渲染这个:

{
  "isCompany": false,
  "accommodations": [
    {
      "id": "00000000031000000067",
      "isChecked": false,
      "name": "5 JULI 2017",
      "addressLine1": "STRAAT 10 ",
      "addressLine2": "1000 New York",
      "nightsDeclared": 0,
      "schoolNightsDeclared": 0,
      "schoolNightsAttached": 0,
      "taxableNights": 0.0,
      "totalPayment": 0.0,
      "isInProgress": false,
      "isLate": false,
      "isPayed": "false",
      "deadline": "2021-12-31",
      "initialAmount": 0.0,
      "remainingAmount": 0.0
    },
    {
      "id": "00000000031000006362",
      "isChecked": false,
      "name": "BELLEVIE",
      "addressLine1": "STRAAT 10 ",
      "addressLine2": "1000 New York",
      "nightsDeclared": 0,
      "schoolNightsDeclared": 0,
      "schoolNightsAttached": 0,
      "taxableNights": 0.0,
      "totalPayment": 0.0,
      "isInProgress": false,
      "isLate": false,
      "isPayed": "false",
      "deadline": "2021-12-31",
      "initialAmount": 0.0,
      "remainingAmount": 0.0
    }
  ]
}

我已经从 HTML 内部的 div 中获得了这个完整的 JSON 数组,方法如下:

.check(css("section.ht-declarations-tab-content-container>div#DATA--DECL-DATA").saveAs("jsonObj"))

然后渲染结果我写了这个:

.exec { session => println("json = " + session("jsonObj").as[String]); session }.exitHereIfFailed

然而,如前所述,我得到了完整的 JSON 数组。

如何只获取第一个对象的第一个ID元素?所以基本上:00000000031000000067

我会用 Jackson 做那份工作:

private static final ObjectMapper MAPPER = new ObjectMapper();

public static String getFirstId(String json) throws JsonProcessingException {
    return MAPPER.readTree(json).get("accommodations").get(0).get("id").asText();
}

那么你可以这样做:

System.out.println(getFirstId(json));

输出:

00000000031000000067

如果你想将 de JSON 打印成一棵树,你可以这样做:

public static String toTree(String json) throws JsonProcessingException {
    return MAPPER.readTree(json).toPrettyString();
}

那么你可以这样做:

System.out.println(toTree(json));

输出:

{
  "isCompany" : false,
  "accommodations" : [ {
    "id" : "00000000031000000067",
    "isChecked" : false,
    "name" : "5 JULI 2017",
    "addressLine1" : "STRAAT 10 ",
    "addressLine2" : "1000 New York",
    "nightsDeclared" : 0,
    "schoolNightsDeclared" : 0,
    "schoolNightsAttached" : 0,
    "taxableNights" : 0.0,
    "totalPayment" : 0.0,
    "isInProgress" : false,
    "isLate" : false,
    "isPayed" : "false",
    "deadline" : "2021-12-31",
    "initialAmount" : 0.0,
    "remainingAmount" : 0.0
  }, {
    "id" : "00000000031000006362",
    "isChecked" : false,
    "name" : "BELLEVIE",
    "addressLine1" : "STRAAT 10 ",
    "addressLine2" : "1000 New York",
    "nightsDeclared" : 0,
    "schoolNightsDeclared" : 0,
    "schoolNightsAttached" : 0,
    "taxableNights" : 0.0,
    "totalPayment" : 0.0,
    "isInProgress" : false,
    "isLate" : false,
    "isPayed" : "false",
    "deadline" : "2021-12-31",
    "initialAmount" : 0.0,
    "remainingAmount" : 0.0
  } ]
}

如果您的 JSON 嵌入在 HTML 中,您有 2 种可能性:

  1. 像您目前所做的那样使用 css 检查从 HTML 中提取 JSON,然后使用 transform 解析 JSON 并提取所需的值,例如使用其他答案中建议的 Jackson
  2. 使用regex一次性提取想要的值,例如check(regex(""""accommodations":\[\{"id":"(.*?)""""))

这可以通过几个步骤完成。

  1. 一开始只需要提取json并更改响应体。
  2. 然后您可以使用简单的 jsonPath 并使用 json

更改 - 使用 transformResponse 提取 json 字符串并删除断行并设置为新的响应正文

http("...")
    .get("...")
    .transformResponse { (response, _) =>
      val json = response.body.string match {
        case s"""<div id="DATA--DECL-DATA">${json}</div>""" => json.replaceAll("\n", "")
      }

      response.copy(body = new StringResponseBody(json, response.body.charset))
    }
    .check(jsonPath("$...").find.saveAs("..."))

我建议使用 circe 来解析 JsonString,有两种方法可以做到这一点:

  1. 要求您将以下包添加到您的 sbt 构建中:
val core = "io.circe" %% "circe-core" % circeVersion
val generic = "io.circe" %% "circe-generic" % circeVersion
val parser = "io.circe" %% "circe-parser" % circeVersion

addCompilerPlugin("org.scalamacros" % "paradise" % "2.1.1" cross CrossVersion.full),

此方法使用大小写 类 和字段提取来获取您想要的对象。 Case 类 可以专门定义您想要的字段或将整个 json 对象表示为文档。此解决方案为您提供了很大的灵活性,但需要大量样板

// SOLUTION 1:

import io.circe.parser.decode
import io.circe.generic.JsonCodec

/** 
  * First Build two case classes to represent 
  * the JSON object that you will be parsing to
  */

@JsonCodec
case class Accommodation(id: String)

@JsonCodec
case class Response(isCompany: Boolean, accommodations: List[Accommodation]) {
    def getFirstAccomodationID: Option[String] = {
        this.accommodations.headOption match {
            case Some(accommodation) => Some(accommodation.id)
            case _ => None
        }

    }
}

/* In order to actually pull the data */
val response: Either[io.circe.Error,Response] = decode[Response](jsonString)
println(response.right.get.getFirstAccomodationID)
  1. 需要您添加以下包
val core = "io.circe" %% "circe-core" % circeVersion
val generic = "io.circe" %% "circe-generic" % circeVersion
val parser = "io.circe" %% "circe-parser" % circeVersion
val optics = "io.circe" %% "circe-optics" % circeVersion

此解决方案使用 json 解析和遍历来获取所需的确切字段,并允许您准确指定所需的字段。

对于阅读代码的人来说,此解决方案也更紧凑且更易于推理。

// SOLUTION 2:

import io.circe._,
import io.circe.parser._

/* First convert the String into a JSON object */
val json: Json = parse(jsonString).getOrElse(Json.Null)

import io.circe.optics.JsonPath._

/* Use Circe Optics to define the json path you wish to traverse */
val _getFirstAccomodationID = root.accommodations.each.id.string

/* Finally, fetch the actual String value */
val firstId: Option[String] = _getFirstAccomodationID.getAll(json).headOption
println(firstId)