3

I am looking to extract certain values from a JSON path of arrays and objects and use these values for further processing and am struggling with accessing those elements. Here is the JSON response:

[  
 {  
  "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
     }
    ]
 }
]

I tried accessing the root elements with $ but I could not get further with it.

Here is the test method that I have written. I want to extract the value for od_pair and within each od_pair, I need to be able to retrieve the bucket codes and their available numbers.

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);
        }
    }

}

}

This is the error I see:

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

Can someone help me please?

6
  • I need to be able to retrieve the bucket codes and their available numbers. Base on od_pair, right? Commented Jul 30, 2019 at 13:12
  • I want to be able to access not just the od_pair but also the bucket objects within. So each od_pair has many buckets as objects. With your solution of Hashmap, I am not sure how I can reach out to the various buckets within each od_pairs Commented Jul 30, 2019 at 13:12
  • Yes thats correct @Fenio Commented Jul 30, 2019 at 13:12
  • Okay, got it. There are a few solutions but I'll show you extended and the best (in my opinion) one. I need a few minutes Commented Jul 30, 2019 at 13:13
  • @Fenio: So basically I am trying to run nested for loops, one for getting the od_pair and within that a for loop for buckets to get each bucket code and available number. Apologies as I am a newbie and hence some of my questions may sound naive here. Commented Jul 30, 2019 at 13:14

3 Answers 3

2

I would suggest parsing your JSON into Java classes to ease the processing.

How to do that? First, we need to create Java classes which will represent the JSON you provided.

Let's analyze the JSON. Starts with an array. The array contains multiple JSON Object. These objects contain od_pair value and array of objects called buckets.

Let's create a class (you can name it whatever you want) 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!
}

This class represents a single JSON Object in the main Array. It contains od_pair value AND nested JSON Array but in Java representation -> List of BucketObject classes. Let's create BucketObject class:

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

We have only 3 values in each of the objects.

Now, it's time to parse JSON into the written classes.

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

Remember that Pair is a single JSON Object. That's why we start parsing from the root represented by dollar sign $ and we declare that JSON should be parsed into an ARRAY of Pair objects!

Now, processing will be much simpler!

I am not sure what do you need, but I will show you an example of how to get data from the buckets based on od_pair field and you should be able to figure out the rest of the processing.

So, we have the array of Pair class: Pair[] pairs;

Now, we want to get 1 Pair object based on od_pair value.

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

        throw new NoSuchElementException();
    }

Now, we have the Pair object. We can access buckets for this object using

List<BucketObject> buckets = pair.buckets;

The rest of the processing is iterating over List<BucketObject> and getting desired values.

Hope it helps!

Sign up to request clarification or add additional context in comments.

8 Comments

Thank you very much @Fenio, I shall implement this approach and update on how it goes.
@Mihir If you have more troubles - create another question. I scan questions tagged rest assured regularly :)
Ok sure will do @Fenio. Thank you very much
Thank you very much for your solution. just for my understanding and learning, would you also be able to advice on the for loop code that I had written earlier. Where I had nested for loops one for getting the od_pair and one for getting bucket information please? What was I missing there or where was I going wrong? @Fenio
@Mihir I'll check tomorrow
|
1

OP asked me to advise on how to fix his code, hence the second answer.

Let's analyze the code you provided:

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);
        }
    }

}

I am not using compilator right now, so I can miss a few things.

#1

First thing I can see is that you save the main array into the List<Object> List<Object> LegList = jsonPath.getList("$");

Instead, you could save it to more understandable type, since Object is so generic, you have no idea what's inside it.

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

#2 The for loop looks incorrect because of the evaluator j <= NoofLegs;. This will probably cause IndexArrayOutOfBoundsException or something similar. With the given code, if you have 4 legs, the for loop will try to process 5 legs which are incorrect.

#3 Similar to the #1, line where you save the bucket list List<Object> BucketsList = jsonPath.param("j", j).getList("[j].buckets"); Could also be changed to List<HashMap<String, Object>> instead.

If you'd do that, you wouldn't need integer-based nested for loop.

You see, the HashMap<String, Object> is actually crucial to parse nested objects. The String is just a name like buckets or od_pair. It's the JSON representation. The second argument Object is different. RestAssured returns different types within the HashMap, that's why we use Object instead of String. Sometimes it's not String.

Example based on your JSON:

Collect buckets to List of HashMaps:

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

Each of the HashMap in the list is a representation of this:

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

The Object in HashMap is either String or Integer in your case. So, if you get element bucket from a HashMap you'll get its value.

Let's combine it with for loop for further clarification:

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");
}

3 Comments

Thank you very much for the explanation. Your explanation is very clear and helps in understanding this so well. Thank you very much for your help Sir!
@Mihir You are very welcome :) Feel free to ask more questions if you need help
I loved this above approach you have provided. I just have one question on this. I've added a section below again. If you could kindly reply.@Fenio
0

Looking at the error message, it looks like you are using rest-assured and that the JsonPath class is io.restassured.path.json.JsonPath from the rest-assured library.

I'm sure you're aware, but (perhaps for other readers) note that this is different from Jayway's json-path and is NOT the com.jayway.jsonpath.JsonPath class from that library.

Also be aware that, as mentioned in the documentation rest-assured uses the Groovy GPath syntax for manipulating/extracting JSON.

With that, I believe the following will extract what you need, i.e. od_pair and their corresponding buckets with available numbers:

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

where for each entry of the map, the key is the od_pair and the value is another map whose key is the bucket and value is the available number. The jsonString is the JSON you provided in the question.

You can iterate through the map to get what you want:

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();
    }
}

Printing out the map you will get:

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

Printing the map as JSON using Gson:

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

you will get

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

For background, the String collectEntries{entry -> [entry.od_pair, entry.buckets.collectEntries{bucketEntry -> [bucketEntry.bucket, bucketEntry.available]}]}

is a Groovy closure that uses methods from the Groovy Collections API: Refer Collection, List and Map

Shout out to @Fenio for the pure Java solution above.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.