Skip to content

Commit 852f9a3

Browse files
committed
refactor: finish operator: filter
1 parent 23a9e55 commit 852f9a3

File tree

2 files changed

+54
-14
lines changed

2 files changed

+54
-14
lines changed

jsonpath/__init__.py

Lines changed: 42 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
Author : zhangxianbing1
33
Date : 2020-12-27 09:22:14
44
LastEditors : zhangxianbing1
5-
LastEditTime : 2020-12-31 14:18:23
5+
LastEditTime : 2020-12-31 17:02:04
66
Description : JSONPath
77
"""
88
__version__ = "1.0.0"
@@ -23,7 +23,7 @@
2323

2424
SEP = ";"
2525
# regex patterns
26-
REP_PICKUP_QUOTE = re.compile(r"['\"](.*?)['\"]")
26+
REP_PICKUP_QUOTE = re.compile(r"['](.*?)[']")
2727
REP_PICKUP_BRACKET = re.compile(r"[\[](.*?)[\]]")
2828
REP_PUTBACK_QUOTE = re.compile(r"#Q(\d+)")
2929
REP_PUTBACK_BRACKET = re.compile(r"#B(\d+)")
@@ -32,11 +32,15 @@
3232

3333

3434
REP_SLICE_CONTENT = re.compile(r"^(-?\d*)?:(-?\d*)?(:-?\d*)?$")
35-
REP_SELECT_CONTENT = re.compile(r"^([^,]+)(,[^,]+)+$")
35+
REP_SELECT_CONTENT = re.compile(r"^([\w.]+)(,[\w.]+)+$")
3636
# operators
3737
REP_UNION_OP = re.compile(r"(?<!\\),")
3838

39-
# pylint: disable=invalid-name,missing-function-docstring,missing-class-docstring
39+
REP_FILTER_FIELD = re.compile(
40+
r"@\.(.*?)(?=<=|>=|==|!=|>|<| in| not| is)|len\(@\.(.*?)\)"
41+
)
42+
43+
# pylint: disable=invalid-name,missing-function-docstring,missing-class-docstring,eval-used
4044

4145

4246
def concat(x, y, con=SEP):
@@ -51,7 +55,6 @@ class JSONPath:
5155
# annotations
5256
result: Iterable
5357
result_type: str
54-
caller_globals: Dict[str, Any]
5558
subx = defaultdict(list)
5659
steps: list
5760
lpath: int
@@ -60,7 +63,6 @@ def __init__(self, expr: str, *, result_type="VALUE"):
6063
if result_type not in RESULT_TYPE:
6164
raise ValueError(f"result_type must be one of {tuple(RESULT_TYPE.keys())}")
6265
self.result_type = result_type
63-
self.caller_globals = sys._getframe(1).f_globals
6466

6567
# parse expression
6668
expr = self._parse_expr(expr)
@@ -101,6 +103,17 @@ def _f_putback_quote(self, m):
101103
def _f_putback_bracket(self, m):
102104
return self.subx["#B"][int(m.group(1))]
103105

106+
@staticmethod
107+
def _f_notvar(m):
108+
return f"{m.group(1)} not in __obj"
109+
110+
@staticmethod
111+
def _f_brackets(m):
112+
ret = "__obj"
113+
for e in m.group(1).split("."):
114+
ret += '["%s"]' % e
115+
return ret
116+
104117
def parse(self, obj):
105118
if not isinstance(obj, (list, dict)):
106119
raise TypeError("obj must be a list or a dict.")
@@ -114,13 +127,23 @@ def parse(self, obj):
114127

115128
return self.result
116129

117-
def _traverse(self, f, obj, idx: int):
130+
@staticmethod
131+
def _traverse(f, obj, istep: int, *args):
118132
if isinstance(obj, list):
119-
for i, v in enumerate(obj):
120-
f(v, idx)
133+
for v in obj:
134+
f(v, istep, *args)
121135
elif isinstance(obj, dict):
122-
for k, v in obj.items():
123-
f(v, idx)
136+
for v in obj.values():
137+
f(v, istep, *args)
138+
139+
def _filter(self, obj, istep: int, step):
140+
r = False
141+
try:
142+
r = eval(step, None, {"__obj": obj})
143+
except Exception as err:
144+
print(err)
145+
if r:
146+
self._trace(obj, istep)
124147

125148
def _trace(self, obj, istep: int):
126149
"""Perform operation on object.
@@ -176,8 +199,11 @@ def _trace(self, obj, istep: int):
176199
return
177200

178201
# filter
179-
# elif key.startswith("?(") and key.endswith(")"):
180-
# pass
202+
if step.startswith("?(") and step.endswith(")"):
203+
step = step[2:-1]
204+
step = REP_FILTER_FIELD.sub(self._f_brackets, step)
205+
self._traverse(self._filter, obj, istep + 1, step)
206+
return
181207

182208
# sort
183209
# elif key:
@@ -188,5 +214,7 @@ def _trace(self, obj, istep: int):
188214
# JSONPath("$.a.'b.c'.'d e'.[f,g][h][*][j.k][l m][2:4]..d", result_type="FIELD")
189215
with open("test/data/2.json", "rb") as f:
190216
d = json.load(f)
191-
JSONPath("$.book[1:3].title").parse(d)
217+
JSONPath(
218+
'$.book[?(@.title=="Herman Melville" or @.title=="Evelyn Waugh")].title'
219+
).parse(d)
192220
# JSONPath("$..price").parse(d)

test/conftest.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,18 @@
5353
TestCase("$.book[-1:1]", data, data["book"][-1:1]),
5454
TestCase("$.book[-1:-11:3]", data, data["book"][-1:-11:3]),
5555
TestCase("$.book[:]", data, data["book"][:]),
56+
TestCase("$.book[?(@.price>8 and @.price<9)].price", data, [8.95, 8.99]),
57+
TestCase('$.book[?(@.category=="reference")].category', data, ["reference"]),
58+
TestCase(
59+
'$.book[?(@.category!="reference" and @.price<9)].title',
60+
data,
61+
["Moby Dick"],
62+
),
63+
TestCase(
64+
'$.book[?(@.author=="Herman Melville" or @.author=="Evelyn Waugh")].author',
65+
data,
66+
["Evelyn Waugh", "Herman Melville"],
67+
),
5668
]
5769
)
5870
def cases(request):

0 commit comments

Comments
 (0)