2025, Oct 02 13:17
Почему фильтр JSONPath [?()] ломается в jsonpath-ng и что делать
Почему предикат JSONPath [?()] падает в jsonpath-ng, как отфильтровать по name и получить label без ошибок, и когда стоит перейти на python-jsonpath в Python.
Фильтрация JSON по полю с помощью JSONPath порой кажется обманчиво простой, пока разные реализации не начинают расходиться в синтаксисе. Если вы пытаетесь выбрать объект по имени и извлечь его label, выражение может сработать в одном инструменте, но упасть в вашем Python‑коде. Ниже — краткое объяснение, почему фильтр вроде $..[?(@.name=='is_literate')] может ломаться в jsonpath-ng и как получить стабильный результат.
Воспроизведение: фильтр разбирается онлайн, но падает локально
Задача — найти объект, у которого name равен is_literate, а затем прочитать его label. Используется выражение $..[?(@.name=='is_literate')]. В онлайн‑тестере JSONPath оно отрабатывает корректно, но в jsonpath-ng вызывает ошибку парсера.
from jsonpath_ng.ext import parse
expr_text = "$..[?(@.name=='is_literate')]"
compiled_query = parse(expr_text)
extracted = [hit.value for hit in compiled_query.find(source_payload)]
print(extracted)
Ошибка указывает на символ вопросительного знака, показывая, что такой синтаксис парсер не принимает.
Что происходит с предикатом
Суть — в синтаксисе предиката фильтра [?()]. В jsonpath-ng эта форма в приведённом контексте не поддерживается: парсер спотыкается на токене ?. Замечено, что размещение фильтра сразу после оператора рекурсивного спуска может быть проблематичным, тогда как примеры в документации пакета следуют шаблону вроде $.objects[?(@some_field > 5)], где перед фильтром идёт именованный сегмент. Попытки типа $..*[?(@name=='is_literate')] избегают ошибки разбора, но всё равно не дают ожидаемого результата. В некоторых тестах выражения вроде $[?(@.name=='is_literate')] или даже $[?name=='is_literate'] могут сработать, но исходная форма с $.. и [?()] — нет. Проще говоря, парсер в jsonpath-ng не поддерживает синтаксис с ? в таком использовании.
Решение: используйте библиотеку, которая поддерживает такой фильтр
Переход с jsonpath-ng на python-jsonpath решает проблему. В python-jsonpath тот же предикат выполняется как задумано и возвращает целевой объект, после чего можно прочитать его label по известному name.
import jsonpath
matched_nodes = jsonpath.findall(
    "$..[?(@.name=='is_literate')]",
    source_payload
)
В результате получается ожидаемый массив с объектом, у которого name равен is_literate, что позволяет далее обратиться к его значению label.
Почему это важно
JSONPath выглядит стандартизированным, но библиотеки различаются по поддерживаемому синтаксису и правилам вычисления фильтров. В данном случае предикат с ? работает в одном инструменте и падает в другом. Когда нужно отфильтровать по полю и получить «соседнее» значение вроде label по известному name, такие нюансы определяют, будет ли это одна строка кода или ошибка парсера.
Выводы
Если вам нужно фильтровать по name и забирать label, и выражение выглядит как $..[?(@.name=='is_literate')], ожидайте, что jsonpath-ng споткнётся на предикате. Либо адаптируйте запрос к формам, которые парсятся для вашей структуры данных, либо перейдите на python-jsonpath, где такой фильтр поддерживается и возвращает нужный объект. Понимание поддерживаемого синтаксиса конкретной библиотеки заранее экономит время на выражениях, которые «работают онлайн», но не в вашем продакшене.
Статья основана на вопросе на StackOverflow от Lalit mahato и ответе от Lalit mahato.