Skip to content

Commit 100bf6f

Browse files
committed
fix: resolve issue #15 where filters with bracket notation returned empty results
1 parent 6caa9d4 commit 100bf6f

File tree

2 files changed

+34
-9
lines changed

2 files changed

+34
-9
lines changed

jsonpath/jsonpath.py

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ class JSONPath:
5353
# operators
5454
REP_SLICE_CONTENT = re.compile(r"^(-?\d*)?:(-?\d*)?(:-?\d*)?$")
5555
REP_SELECT_CONTENT = re.compile(r"^([\w.']+)(, ?[\w.']+)+$")
56-
REP_FILTER_CONTENT = re.compile(r"@\.(.*?)(?=<=|>=|==|!=|>|<| in| not| is)|len\(@\.(.*?)\)")
56+
REP_FILTER_CONTENT = re.compile(r"@([.\[].*?)(?=<=|>=|==|!=|>|<| in| not| is)|len\(@([.\[].*?)\)")
5757

5858
# annotations
5959
f: list
@@ -94,15 +94,15 @@ def _parse_expr(self, expr):
9494
# pick up special patterns
9595
expr = JSONPath.REP_GET_QUOTE.sub(self._get_quote, expr)
9696
expr = JSONPath.REP_GET_BACKQUOTE.sub(self._get_backquote, expr)
97+
expr = JSONPath.REP_GET_PAREN.sub(self._get_paren, expr)
9798
expr = JSONPath.REP_GET_BRACKET.sub(self._get_bracket, expr)
9899
expr = re.sub(r"\.(\.#B)", r"\1", expr)
99-
expr = JSONPath.REP_GET_PAREN.sub(self._get_paren, expr)
100100
# split
101101
expr = JSONPath.REP_DOUBLEDOT.sub(f"{JSONPath.SEP}..{JSONPath.SEP}", expr)
102102
expr = JSONPath.REP_DOT.sub(JSONPath.SEP, expr)
103103
# put back
104-
expr = JSONPath.REP_PUT_PAREN.sub(self._put_paren, expr)
105104
expr = JSONPath.REP_PUT_BRACKET.sub(self._put_bracket, expr)
105+
expr = JSONPath.REP_PUT_PAREN.sub(self._put_paren, expr)
106106
expr = JSONPath.REP_PUT_BACKQUOTE.sub(self._put_backquote, expr)
107107
expr = JSONPath.REP_PUT_QUOTE.sub(self._put_quote, expr)
108108
if expr.startswith("$;"):
@@ -147,12 +147,15 @@ def _put_paren(self, m):
147147
@staticmethod
148148
def _gen_obj(m):
149149
content = m.group(1) or m.group(2) # group 2 is for len()
150-
ret = "__obj"
151-
for e in content.split("."):
152-
if len(e) >= 2 and ((e[0] == "'" and e[-1] == "'") or (e[0] == '"' and e[-1] == '"')):
153-
e = e[1:-1]
154-
ret += f'["{e}"]'
155-
return ret
150+
151+
def repl(m):
152+
g = m.group(1)
153+
if g[0] in ("'", '"'):
154+
return f"[{g}]"
155+
return f"['{g}']"
156+
157+
content = re.sub(r"\.(\w+|'[^']*'|\"[^\"]*\")", repl, content)
158+
return "__obj" + content
156159

157160
@staticmethod
158161
def _traverse(f, obj, i: int, path: str, *args):
@@ -275,6 +278,8 @@ def _trace(self, obj, i: int, path):
275278
if step.startswith("?(") and step.endswith(")"):
276279
step = step[2:-1]
277280
step = JSONPath.REP_FILTER_CONTENT.sub(self._gen_obj, step)
281+
if isinstance(obj, dict):
282+
self._filter(obj, i + 1, path, step)
278283
self._traverse(self._filter, obj, i + 1, path, step)
279284
return
280285

tests/test_issues.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,23 @@ def test_issue_16_quoted_keys():
4444
assert JSONPath('$.\'user-list\'[?(@."city-name"=="Austin")]').parse(data) == [
4545
{"city-name": "Austin", "name": "John"}
4646
]
47+
48+
49+
def test_issue_15_bracket_notation_in_filter():
50+
data = {
51+
"tool": {
52+
"books": [
53+
{"properties": [{"name": "moq", "value": "x"}, {"name": "url", "value": "1"}]},
54+
{"properties": [{"name": "url", "value": "3"}]},
55+
{"properties": [{"name": "moq", "value": "q"}, {"name": "url", "value": "4"}]},
56+
]
57+
}
58+
}
59+
60+
# Case 1: Bracket notation in filter
61+
expr = "$['tool']['books'][*]['properties'][*][?(@['name'] == 'moq')]['value']"
62+
assert JSONPath(expr).parse(data) == ["x", "q"]
63+
64+
# Case 2: Dot notation in filter (already working, but good to keep)
65+
expr_dot = "$.tool.books.[*].properties.[*].[?(@.name=='moq')].value"
66+
assert JSONPath(expr_dot).parse(data) == ["x", "q"]

0 commit comments

Comments
 (0)