Выражение JsonPath, чтобы найти, если пара ключ-значение не присутствует в наборе JSON объектов? - PullRequest
1 голос
/ 30 января 2020

Учитывая следующее 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"
        }
      ]
    }
  }
}

Я хотел бы иметь возможность использовать JsonPath , чтобы найти, если какой-либо из объектов в массиве записей отсутствует expiry_date ключ и значение пары. В настоящее время у меня есть рабочее решение, использующее метод, который добавляет все записи record в один список и все записи expiry_date в другой и просто проверяет, равно ли вычитание обоих размеров списка 0, чтобы вернуть логическое значение.

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

Для этого необходимо использовать два выражения JsonPath с разделителем ;, таким образом, что вызывает некоторые другие, не связанные с этим проблемы, которых я бы хотел избежать:

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

Для копии этой точной структуры в XML я могу использовать выражение XPath, например, так: boolean(full_report/history/record[not(expiry_date/text())])

Есть ли шанс, что есть эквивалентный фильтр, который я мог пропустить в JsonPath?

Обновление

Ответ Саида Ализаде, приведенный ниже для использования предиката, не решает проблему необходимости передавать свойство для поиска в первую очередь.

Это помогло сделать метод и передать свойство более понятным, поскольку больше нет необходимости писать два отдельных выражения вместо expression;property_to_lookup. Как видно из метода, приведенного ниже, эта фундаментальная проблема все еще остается, но она намного проще:

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

Я не хочу добавлять третий метод в метод для свойства или разбивать входящую строку обойти проблему, поскольку это создает некоторые проблемы при предоставлении выражения JsonPath в качестве входных данных в конечной точке. Если не существует однострочного выражения, подобного примеру XPath, я отмечу вопрос как ответ от Саида.

1 Ответ

0 голосов
/ 30 января 2020

Как вы можете увидеть пример ниже, используя предикат , вы можете отфильтровать результат:

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

будет напечатан следующий вывод как пропущенная единственная запись " expiry_date"свойство

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

обновление

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

выход:

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