使用嵌套对象访问 jsonpath 元素

Accessing jsonpath elements with nested objects

我希望从数组和对象的 JSON 路径中提取某些值,并将这些值用于进一步处理,但我正在努力访问这些元素。这是 JSON 响应:

[  
 {  
  "od_pair":"7015400:8727100",
  "buckets":[  
     {  
        "bucket":"C00",
        "original":2,
        "available":2
     },
     {  
        "bucket":"A01",
        "original":76,
        "available":0
     },
     {  
        "bucket":"B01",
        "original":672,
        "available":480
     }
    ]
    },
 {  
  "od_pair":"7015400:8814001",
  "buckets":[  
     {  
        "bucket":"C00",
        "original":2,
        "available":2
     },
     {  
        "bucket":"A01",
        "original":40,
        "available":40
     },
     {  
        "bucket":"B01",
        "original":672,
        "available":672
     },
     {  
        "bucket":"B03",
        "original":632,
        "available":632
     },
     {  
        "bucket":"B05",
        "original":558,
        "available":558
     }
    ]
 }
]

我尝试使用 $ 访问根元素,但我无法进一步使用它。

这是我写的测试方法。我想提取 od_pair 的值,并且在每个 od_pair 中,我需要能够检索存储桶代码及其可用编号。

public static void updateBuckets(String ServiceName, String DateOfJourney) throws Exception {
    File jsonExample = new File(System.getProperty("user.dir"), "\LogAvResponse\LogAvResponse.json");
    JsonPath jsonPath = new JsonPath(jsonExample);

    List<Object> LegList = jsonPath.getList("$");
    // List<HashMap<String, String>> jsonObjectsInArray = jsonPath.getList("$");

    int NoofLegs = LegList.size();
    System.out.println("No of legs :" + NoofLegs);
    for (int j = 0; j <= NoofLegs; j++)
    // for (HashMap<String, String> jsonObject : jsonObjectsInArray) {
    {

        String OD_Pair = jsonPath.param("j", j).getString("[j].od_pair");
        // String OD_Pair = jsonObject.get("od_pair");

        System.out.println("OD Pair: " + OD_Pair);
        List<Object> BucketsList = jsonPath.param("j", j).getList("[j].buckets");

        int NoOfBuckets = BucketsList.size();
        // System.out.println("OD Pair: " + OD_Pair);
        System.out.println("no of Buckets: " + NoOfBuckets);

        for (int i = 0; i < NoOfBuckets; i++) {
            String BucketCode = jsonPath.param("j", j).param("i", i).getString("[j].buckets[i].bucket");
            String Available = jsonPath.param("j", j).param("i", i).getString("[j].buckets[i].available");

            int BucketCodeColumn = XLUtils.getBucketCodeColumn(BucketCode);
            int ServiceRow = XLUtils.getServiceRow(ServiceName, DateOfJourney, OD_Pair);
            System.out.println("Row of " + ServiceName + ":" + DateOfJourney + "is:" + ServiceRow);
            System.out.println("Bucket Code column of " + BucketCode + " is: " + BucketCodeColumn);
            XLUtils.updateAvailability(ServiceRow, BucketCodeColumn, Available);
        }
    }

}

}

这是我看到的错误:

Caused by: 
org.codehaus.groovy.control.MultipleCompilationErrorsException: startup 
failed:
Script1.groovy: 1: unexpected token: [ @ line 1, column 27.
restAssuredJsonRootObject.[j].od_pair

有人可以帮我吗?

我建议将您的 JSON 解析为 Java classes 以简化处理过程。

怎么做? 首先,我们需要创建 Java classes 来表示您提供的 JSON。

我们来分析一下JSON。 以数组开头。该数组包含多个 JSON 对象。这些对象包含 od_pair 值和名为 buckets.

的对象数组

让我们创建一个class(你可以随意命名)Pair

public class Pair {
    public String od_pair; //this name is important because it corresponds with the json element's name!
    public List<BucketObject> buckets; //same here!
}

此 class 表示主数组中的单个 JSON 对象。它包含 od_pair 值和嵌套的 JSON 数组,但在 Java 表示中 -> BucketObject class 列表。让我们创建 BucketObject class:

public class BucketObject { //this name is NOT importnat
    public String bucket; //names are important
    public int original;
    public int available;
}

我们在每个对象中只有 3 个值。

现在,是时候将 JSON 解析为书面的 classes 了。

JsonPath path = JsonPath.from(json);
Pair[] pairs = path.getObject("$", Pair[].class);

请记住 Pair 是一个单独的 JSON 对象。这就是为什么我们从美元符号 $ 表示的根开始解析,并且我们声明 JSON 应该被解析为一个包含 Pair 个对象的数组!

现在,处理会简单很多!

我不确定你需要什么,但我会向你展示如何根据 od_pair 字段从桶中获取数据的示例,你应该能够弄清楚其余的处理过程.

所以,我们有 Pair class 的数组:Pair[] pairs;

现在,我们要根据 od_pair 值获得 1 个 Pair 对象。

    public static Pair getPairBasedOnOdPairValue(Pair[] pairs, String odPairValue) {
        for (Pair pair : pairs) {
            if (pair.od_pair.equals(odPairValue)) return pair;
        }

        throw new NoSuchElementException();
    }

现在,我们有了 Pair 对象。我们可以使用

访问此对象的 buckets
List<BucketObject> buckets = pair.buckets;

处理的其余部分是遍历 List<BucketObject> 并获得所需的值。

希望对您有所帮助!

OP 让我建议如何修复他的代码,因此是第二个答案。

让我们分析一下您提供的代码:

public static void updateBuckets(String ServiceName, String DateOfJourney) throws Exception {
    File jsonExample = new File(System.getProperty("user.dir"), "\LogAvResponse\LogAvResponse.json");
    JsonPath jsonPath = new JsonPath(jsonExample);

    List<Object> LegList = jsonPath.getList("$");
    // List<HashMap<String, String>> jsonObjectsInArray = jsonPath.getList("$");

    int NoofLegs = LegList.size();
    System.out.println("No of legs :" + NoofLegs);
    for (int j = 0; j <= NoofLegs; j++)
    // for (HashMap<String, String> jsonObject : jsonObjectsInArray) {
    {

        String OD_Pair = jsonPath.param("j", j).getString("[j].od_pair");
        // String OD_Pair = jsonObject.get("od_pair");

        System.out.println("OD Pair: " + OD_Pair);
        List<Object> BucketsList = jsonPath.param("j", j).getList("[j].buckets");

        int NoOfBuckets = BucketsList.size();
        // System.out.println("OD Pair: " + OD_Pair);
        System.out.println("no of Buckets: " + NoOfBuckets);

        for (int i = 0; i < NoOfBuckets; i++) {
            String BucketCode = jsonPath.param("j", j).param("i", i).getString("[j].buckets[i].bucket");
            String Available = jsonPath.param("j", j).param("i", i).getString("[j].buckets[i].available");

            int BucketCodeColumn = XLUtils.getBucketCodeColumn(BucketCode);
            int ServiceRow = XLUtils.getServiceRow(ServiceName, DateOfJourney, OD_Pair);
            System.out.println("Row of " + ServiceName + ":" + DateOfJourney + "is:" + ServiceRow);
            System.out.println("Bucket Code column of " + BucketCode + " is: " + BucketCodeColumn);
            XLUtils.updateAvailability(ServiceRow, BucketCodeColumn, Available);
        }
    }

}

我现在没有使用编译器,所以我可能会遗漏一些东西。

#1

我首先看到的是您将主数组保存到 List<Object> List<Object> LegList = jsonPath.getList("$");

相反,您可以将它保存为更易于理解的类型,因为 Object 是如此通用,您不知道它里面有什么。

List<HashMap<String, Object>> LegList = jsonPath.getList("$");

#2 由于计算器,for 循环看起来不正确 j <= NoofLegs;。 这可能会导致 IndexArrayOutOfBoundsException 或类似的情况。使用给定的代码,如果您有 4 条腿,for 循环将尝试处理 5 条不正确的腿。

#3 类似于 #1,保存遗愿清单的行 List<Object> BucketsList = jsonPath.param("j", j).getList("[j].buckets"); 也可以改为 List<HashMap<String, Object>>

如果你这样做,你就不需要基于整数的嵌套 for 循环。

你看,HashMap<String, Object> 实际上对解析嵌套对象至关重要。字符串只是一个名称,如 bucketsod_pair。这是 JSON 表示。第二个参数 Object 不同。 RestAssured returns HashMap 中的不同类型,这就是我们使用 Object 而不是 String 的原因。有时它不是字符串。

示例基于您的JSON:

将桶收集到 HashMap 列表:

List<HashMap<String, Object>> bucketsList = jsonPath.param("j", j).getList("[j].buckets");

列表中的每个 HashMap 都是这样的表示:

{  
        "bucket":"C00",
        "original":2,
        "available":2
},

HashMap 中的 Object 在您的情况下是 StringInteger。 因此,如果您从 HashMap 中获取元素 bucket,您将获取其值。

让我们将其与 for 循环结合起来进一步说明:

List<HashMap<String, Object>> bucketsList = jsonPath.param("j", j).getList("[j].buckets");
for (HashMap<String, Object> singleBucket : bucketsList) {
    String firstValue = (String) singleBucket.get("bucket");
    Integer secondValue = (Integer) singleBucket.get("original");
}

看错误信息,好像你用的是rest-assured,而JsonPath class是来自放心库的io.restassured.path.json.JsonPath

我相信您知道,但是(可能对其他读者而言)请注意,这与 Jayway's json-path 不同,并且不是该库中的 com.jayway.jsonpath.JsonPath class。

另请注意,如 manipulating/extracting JSON 的 documentation rest-assured uses the Groovy GPath 语法中所述。

这样,我相信以下内容将提取您需要的内容,即 od_pair 及其相应的可用数字桶:

Map<String, Map<String, Integer>> map = JsonPath.with(jsonString).get("collectEntries{entry -> [entry.od_pair, entry.buckets.collectEntries{bucketEntry -> [bucketEntry.bucket, bucketEntry.available]}]}");

对于映射的每个条目,键是 od_pair,值是另一个映射,其键是桶,值是可用数量。 jsonString 是您在问题中提供的 JSON。

您可以遍历地图以获得您想要的内容:

for(Map.Entry<String, Map<String, Integer>> entry : map.entrySet())
{
    String od_pair = entry.getKey();

    Map<String, Integer> bucketMap = entry.getValue();

    for(Map.Entry<String, Integer> bucketEntry : bucketMap.entrySet())
    {
        String bucket = bucketEntry.getKey();
        int available = bucketEntry.getValue();
    }
}

打印地图你会得到:

{7015400:8727100={C00=2, A01=0, B01=480}, 7015400:8814001={C00=2, A01=40, B01=672, B03=632, B05=558}}

使用 Gson 将地图打印为 JSON:

System.out.println(new GsonBuilder().setPrettyPrinting().create().toJson(map));

你会得到

{
  "7015400:8727100": {
    "C00": 2,
    "A01": 0,
    "B01": 480
  },
  "7015400:8814001": {
    "C00": 2,
    "A01": 40,
    "B01": 672,
    "B03": 632,
    "B05": 558
  }
}

对于背景,字符串 collectEntries{entry -> [entry.od_pair, entry.buckets.collectEntries{bucketEntry -> [bucketEntry.bucket, bucketEntry.available]}]}

是一个 Groovy 闭包,它使用 Groovy 集合中的方法 API:参考 Collection, List and Map

向@Fenio 大声喊出上述纯 Java 解决方案。