简体   繁体   中英

JsonPath expression to find if a key-value pair is not present in a set of JSON objects?

Given the following JSON:

{
  "full_report": {
    "score": "5",
    "history": {
      "record": [
        {
          "name": "John Smith",
          "SSN": "123456789",
          "last_visit": "02.03.2019",
          "expiry_date": "15.03.2019"
        },
        {
          "name": "John Doe",
          "SSN": "987654321",
          "last_visit": "05.03.2019",
          "expiry_date": "15.09.2019"
        },
        {
          "name": "Jane Doe",
          "SSN": "999999999",
          "last_visit": "02.03.2019"
        }
      ]
    }
  }
}

I would like to be able to use JsonPath to find if any of the objects under the record array are missing the expiry_date key and value pair. I currently have a working solution using a method that adds all entries of record to one list and all entries of expiry_date to another and simply checks if the subtraction of both list sizes equals 0 to return a boolean value.

    public Boolean isMissing(String json, String expression) {
        String[] expr = expression.split(";");
        List<String> totalRecords = JsonPath.read(json, expr[0]);
        List<String> foundRecords = JsonPath.read(json, expr[1]);

        return totalRecords.size() - foundRecords.size() != 0;
    }

This requires using two JsonPath expressions with a ; delimiter like so which causes some other, unrelated issues that I'd like to avoid:

$.full_report.history.record[?(@.expiry_date)];$.full_report.history.record[*]

For a copy of this exact structure in XML I can use an XPath expression like so: boolean(full_report/history/record[not(expiry_date/text())])

Any chance there's an equivalent filter that I may have missed in JsonPath?

Update

The answer below by Saeed Alizadeh to use predicate does not solve the problem of having to pass in the property to look for in the first place.

It did help make the method and passing in the property a fair bit cleaner as there's no longer a need to write two separate expressions rather than expression;property_to_lookup . As seen in the method below, that fundamental issue still remains but is much more hassle-free:

public Boolean isMissing(String json, String expression) {
    String[] expressionAndPredicate = expression.split(";");

    Predicate missingKeyValue = predicateContext -> {
        Object missingKey = predicateContext.item(Map.class).get(expressionAndPredicate[1]);
        return missingKey == null || missingKey.toString().length() <= 0;
        };

    List<String> records = JsonPath.read(json, expressionAndPredicate[0], missingKeyValue);
    return records.size() != 0;
}

I don't want to add a 3rd parameter to the method for the property nor split the incoming string to sidestep the issue as it creates some issues when providing the JsonPath expression as input at the endpoint. If no one-line expression exists similar to the XPath example, I'll mark the question as answered by Saeed.

As you can see example below by using predicate you can filter result:

import com.jayway.jsonpath.JsonPath;
import com.jayway.jsonpath.Predicate;

import java.util.List;
import java.util.Map;

public class Main {
    public static void main(String[] args) {
        String json = "{\n" +
                "  \"full_report\": {\n" +
                "    \"score\": \"5\",\n" +
                "    \"history\": {\n" +
                "      \"record\": [\n" +
                "        {\n" +
                "          \"name\": \"John Smith\",\n" +
                "          \"SSN\": \"123456789\",\n" +
                "          \"last_visit\": \"02.03.2019\",\n" +
                "          \"expiry_date\": \"15.03.2019\"\n" +
                "        },\n" +
                "        {\n" +
                "          \"name\": \"John Doe\",\n" +
                "          \"SSN\": \"987654321\",\n" +
                "          \"last_visit\": \"05.03.2019\",\n" +
                "          \"expiry_date\": \"15.09.2019\"\n" +
                "        },\n" +
                "        {\n" +
                "          \"name\": \"Jane Doe\",\n" +
                "          \"SSN\": \"999999999\",\n" +
                "          \"last_visit\": \"02.03.2019\"\n" +
                "        }\n" +
                "      ]\n" +
                "    }\n" +
                "  }\n" +
                "}";
        Predicate expiry_date = new Predicate() {
            public boolean apply(PredicateContext predicateContext) {
                Object expiry_date = predicateContext.item(Map.class).get("expiry_date");
                return expiry_date != null && expiry_date.toString().length() > 0 ? false : true;
            }
        };
        List records = JsonPath.parse(json).read("full_report.history.record[?]", List.class, expiry_date);
        System.out.println(records);
    }
}

this will print following output as the only record missing " expiry_date " property

[{"name":"Jane Doe","SSN":"999999999","last_visit":"02.03.2019"}]

update

public static boolean isMissing(String json, final String jsonProperty) {
    Predicate predicate = new Predicate() {
        public boolean apply(PredicateContext predicateContext) {
            Object propertyObject = predicateContext.item(Map.class).get(jsonProperty);
            return propertyObject != null && propertyObject.toString().length() > 0 ? false : true;
        }
    };
    List records = JsonPath.parse(json).read("full_report.history.record[?]", List.class, predicate);
    return (records != null && records.size() > 0) ? true : false;
}

output:

System.out.println(isMissing(json, "expiry_date")); // prints true
System.out.println(isMissing(json, "name")); // prints false

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM