Get List by using Jsonpath

16,408

Taking into account the JSON you provided and reading through the documentation of Jayway JsonPath. I found two solution that could help resolve you issue.

  1. Solution 1 allows you to directly access the errorMsg field but, not applicbable when there are more than one companies as you have to manually loop
  2. Solution 2 allows constructions of a Custom predicate that is specific to your case and is somewhat generic.

Solution 1 The filter that you have seem to be correct, as shown below:

Filter responseFilter = Filter.filter(Criteria.where("errorMsg").is("INACTIVE"));

Looking into your JSON, payload can be thought of as starting point and the structure seem to be fixed the second value of payload array is failed companies and that is what you are interested in hence, I would write the json path as follow:

Map<String, Object> inactiveFailedCompany =  
           parse(json).read("$.payload[1].['Failed company(ies)'][1]", responseFilter);

If you notice above, I have specified payload[1] and I assume that is how your JSON is structured. I have also specified ['Failed company(ies)'][1] and that is to access the company that has INACTIVE. You can obviously use this solution with a loop to loop through index of ['Failed company(ies)'][1] and print the ID as follow:

System.out.println("Company Object > " + inactiveFailedCompany.toString());
//output: Output > {id=COM002FILE, index=NA, errorMsg=[["INACTIVE"],["INVALID_LOCATION","INACTIVE"]]}

System.out.println("Company ID > " + inactiveFailedCompany.get("id"));
//output: COM002FILE

Looking at the above solution, I felt bad because it is not good enough, so I read more in the documentation and came across Roll your own predicate hence the Solution 2.

Solution 2

As explained in the documentation, I tailored the predicate's apply method to meet your requirements and the solution is as follow:

Predicate inactiveCompaniesPredicate = new Predicate() {
            @Override
            public boolean apply(PredicateContext ctx) {
                if(ctx.item(Map.class).containsKey("errorMsg") && 
                    ((JSONArray)((JSONArray) ctx.item(Map.class).get("errorMsg")).get(0)).get(0).equals("INACTIVE")) {
                    return true; 
                } else {
                    return false; 
                }
            }
        };

Now use the above predicate as follow:

List<Map<String, Object>> failedCompanies = 
           parse(json).read("$.payload[1].['Failed company(ies)'][?]", List.class, inactiveCompaniesPredicate);

And then loop through list of companies and print the ID of the failed companies as follow:

for(Map<String, Object> object: failedCompanies) {
    System.out.println(object.get("id"));
    //output: COM002FILE
}

I would like to reflect on the scary if condition inside the predicate as follow:

Left Part of if condition - xx

This part filters all those that contain errorMsg field.

if(ctx.item(Map.class).containsKey("errorMsg") && yy)

Right Part of if condition - yy

This part makes sure that the errorMsg is INACTIVE

if(xx & ((JSONArray)((JSONArray) ctx.item(Map.class).get("errorMsg")).get(0)).get(0).equals("INACTIVE"))

Here is the sample code I used to test (I imported Jsonpath 2.1 and its dependencies as external jars to my eclipse)

import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
import java.util.Map;
import net.minidev.json.JSONArray;
import com.jayway.jsonpath.Predicate;
import static com.jayway.jsonpath.JsonPath.parse;

public class JaywayJSON {
    public static void main(String[] args) throws IOException {
        String json = readFile("C:\\test_stackoverflow\\jayway.json",
                Charset.defaultCharset());

        /*
         * //Solution 1
         * 
         * Filter responseFilter =
         * Filter.filter(Criteria.where("errorMsg").is("INACTIVE"));
         * 
         * Map<String, Object> inactiveFailedCompany =
         * parse(json).read("$.payload[1].['Failed company(ies)'][1]",
         * responseFilter);
         * 
         * System.out.println("Company Object > " +
         * inactiveFailedCompany.toString()); //output: Output > {id=COM002FILE,
         * index=NA, errorMsg=[["INACTIVE"],["INVALID_LOCATION","INACTIVE"]]}
         * 
         * System.out.println("Company ID > " +
         * inactiveFailedCompany.get("id")); //output: COM002FILE
         */

        // solution 2
        Predicate inactiveCompaniesPredicate = new Predicate() {
            @Override
            public boolean apply(PredicateContext ctx) {
                if (ctx.item(Map.class).containsKey("errorMsg")
                        && ((JSONArray) ((JSONArray) ctx.item(Map.class).get(
                                "errorMsg")).get(0)).get(0).equals("INACTIVE")) {
                    return true;
                } else {
                    return false;
                }
            }
        };

        List<Map<String, Object>> failedCompanies = parse(json).read(
                "$.payload[1].['Failed company(ies)'][?]", List.class,
                inactiveCompaniesPredicate);

        for (Map<String, Object> object : failedCompanies) {
            System.out.println(object.get("id"));
        }
    }

    public static String readFile(String path, Charset encoding)
            throws IOException {
        byte[] encoded = Files.readAllBytes(Paths.get(path));
        return new String(encoded, encoding);
    }
}
Share:
16,408
Abhishek Singh
Author by

Abhishek Singh

Updated on June 30, 2022

Comments

  • Abhishek Singh
    Abhishek Singh almost 2 years

    I am using Jayway JsonPath 2.1.0 to parse a JSON String using JsonPath.

    The JSON Structure is as follows:

    {
        "status": "",
        "source": "",
        "processedRecords": 1,
        "totalRecords": 4,
        "description": "1 company(ies) added/updated, and 2 company(ies) failed",
        "payload": [{
            "Added/updated company(ies)": [
                "COM001FILE"
            ]
        }, {
            "Failed company(ies)": [{
                "id": "COM003FILE",
                "index": "NA",
                "errorMsg": [
                    [
                        "INVALID_LOCATION"
                    ]
                ]
            }, {
                "id": "COM002FILE",
                "index": "NA",
                "errorMsg": [
                    [
                        "INACTIVE"
                    ],
                    [
                        "INVALID_LOCATION",
                        "INACTIVE"
                    ]
                ]
            }]
        }]
    }
    

    Now the requirement is to get a list of ids whose errorMsg contains INACTIVE. to achieve this I am using filters as shown below.

    Filter resposneFilter = Filter.filter(Criteria.where("errorMsg").is(
                "INACTIVE"));
    List<Map<String, Object>> respons = JsonPath.parse(response).read(
                "$.payload[*]", resposneFilter);
    

    but as output I am getting all the values in payload.

    Is it possible to get the expected results?