Skip to content

Commit 23a9e55

Browse files
committed
refactor: finish operator: slice & select
1 parent a188803 commit 23a9e55

File tree

2 files changed

+58
-39
lines changed

2 files changed

+58
-39
lines changed

jsonpath/__init__.py

Lines changed: 52 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,13 @@
22
Author : zhangxianbing1
33
Date : 2020-12-27 09:22:14
44
LastEditors : zhangxianbing1
5-
LastEditTime : 2020-12-31 11:07:08
5+
LastEditTime : 2020-12-31 14:18:23
66
Description : JSONPath
77
"""
88
__version__ = "1.0.0"
99
__author__ = "zhangxianbing"
1010

1111
import json
12-
import logging
13-
import os
1412
import re
1513
import sys
1614
from typing import Any, Dict, Iterable
@@ -29,12 +27,15 @@
2927
REP_PICKUP_BRACKET = re.compile(r"[\[](.*?)[\]]")
3028
REP_PUTBACK_QUOTE = re.compile(r"#Q(\d+)")
3129
REP_PUTBACK_BRACKET = re.compile(r"#B(\d+)")
32-
REP_DOUBLEDOTS = re.compile(r"\.\.")
30+
REP_DOUBLEDOT = re.compile(r"\.\.")
3331
REP_DOT = re.compile(r"(?<!\.)\.(?!\.)")
34-
REP_FILTER = re.compile(r"\[(\??\(.*?\))\]")
35-
REP_SORT = re.compile(r"\[[\\|/](.*?),\]")
3632

3733

34+
REP_SLICE_CONTENT = re.compile(r"^(-?\d*)?:(-?\d*)?(:-?\d*)?$")
35+
REP_SELECT_CONTENT = re.compile(r"^([^,]+)(,[^,]+)+$")
36+
# operators
37+
REP_UNION_OP = re.compile(r"(?<!\\),")
38+
3839
# pylint: disable=invalid-name,missing-function-docstring,missing-class-docstring
3940

4041

@@ -52,8 +53,8 @@ class JSONPath:
5253
result_type: str
5354
caller_globals: Dict[str, Any]
5455
subx = defaultdict(list)
55-
ops: list
56-
oplen: int
56+
steps: list
57+
lpath: int
5758

5859
def __init__(self, expr: str, *, result_type="VALUE"):
5960
if result_type not in RESULT_TYPE:
@@ -63,17 +64,17 @@ def __init__(self, expr: str, *, result_type="VALUE"):
6364

6465
# parse expression
6566
expr = self._parse_expr(expr)
66-
self.ops = expr.split(SEP)
67-
self.oplen = len(self.ops)
68-
print(f"operations : {self.ops}")
67+
self.steps = expr.split(SEP)
68+
self.lpath = len(self.steps)
69+
print(f"steps : {self.steps}")
6970

7071
def _parse_expr(self, expr):
7172
if __debug__:
7273
print(f"before expr : {expr}")
7374

7475
expr = REP_PICKUP_QUOTE.sub(self._f_pickup_quote, expr)
7576
expr = REP_PICKUP_BRACKET.sub(self._f_pickup_bracket, expr)
76-
expr = REP_DOUBLEDOTS.sub(f"{SEP}..{SEP}", expr)
77+
expr = REP_DOUBLEDOT.sub(f"{SEP}..{SEP}", expr)
7778
expr = REP_DOT.sub(SEP, expr)
7879
expr = REP_PUTBACK_BRACKET.sub(self._f_putback_bracket, expr)
7980
expr = REP_PUTBACK_QUOTE.sub(self._f_putback_quote, expr)
@@ -109,15 +110,10 @@ def parse(self, obj):
109110
else:
110111
self.result = []
111112

112-
self._operate(obj, 0)
113+
self._trace(obj, 0)
113114

114115
return self.result
115116

116-
def _op(self, i):
117-
if i < self.oplen:
118-
return self.ops[i]
119-
return None
120-
121117
def _traverse(self, f, obj, idx: int):
122118
if isinstance(obj, list):
123119
for i, v in enumerate(obj):
@@ -126,7 +122,7 @@ def _traverse(self, f, obj, idx: int):
126122
for k, v in obj.items():
127123
f(v, idx)
128124

129-
def _operate(self, obj, idx: int):
125+
def _trace(self, obj, istep: int):
130126
"""Perform operation on object.
131127
132128
Args:
@@ -135,45 +131,62 @@ def _operate(self, obj, idx: int):
135131
"""
136132

137133
# store
138-
if idx >= self.oplen:
134+
if istep >= self.lpath:
139135
self.result.append(obj)
140136
print(obj)
141137
return
142138

143-
op = self.ops[idx]
139+
step = self.steps[istep]
144140

145141
# wildcard
146-
if op == "*":
147-
self._traverse(self._operate, obj, idx + 1)
142+
if step == "*":
143+
self._traverse(self._trace, obj, istep + 1)
144+
return
148145

149146
# recursive descent
150-
elif op == "..":
151-
self._operate(obj, idx + 1)
152-
self._traverse(self._operate, obj, idx)
153-
154-
# get value from dict
155-
elif isinstance(obj, dict) and op in obj:
156-
self._operate(obj[op], idx + 1)
147+
if step == "..":
148+
self._trace(obj, istep + 1)
149+
self._traverse(self._trace, obj, istep)
150+
return
157151

158152
# get value from list
159-
elif isinstance(obj, list) and op.isdigit():
160-
ikey = int(op)
153+
if isinstance(obj, list) and step.isdigit():
154+
ikey = int(step)
161155
if ikey < len(obj):
162-
self._operate(obj[ikey], idx + 1)
156+
self._trace(obj[ikey], istep + 1)
157+
return
163158

164-
# elif key.startswith("?(") and key.endswith(")"): # filter
165-
# pass
159+
# get value from dict
160+
if isinstance(obj, dict) and step in obj:
161+
self._trace(obj[step], istep + 1)
162+
return
163+
164+
# slice
165+
if isinstance(obj, list) and REP_SLICE_CONTENT.fullmatch(step):
166+
vals = eval(f"obj[{step}]")
167+
for v in vals:
168+
self._trace(v, istep + 1)
169+
return
170+
171+
# select
172+
if isinstance(obj, dict) and REP_SELECT_CONTENT.fullmatch(step):
173+
for k in step.split(","):
174+
if k in obj:
175+
self._trace(obj[k], istep + 1)
176+
return
166177

167-
# elif key: # sort
178+
# filter
179+
# elif key.startswith("?(") and key.endswith(")"):
168180
# pass
169181

170-
# elif key: # slice
182+
# sort
183+
# elif key:
171184
# pass
172185

173186

174187
if __name__ == "__main__":
175188
# JSONPath("$.a.'b.c'.'d e'.[f,g][h][*][j.k][l m][2:4]..d", result_type="FIELD")
176189
with open("test/data/2.json", "rb") as f:
177190
d = json.load(f)
178-
# JSONPath("$.book[1].title").parse(d)
179-
JSONPath("$..price").parse(d)
191+
JSONPath("$.book[1:3].title").parse(d)
192+
# JSONPath("$..price").parse(d)

test/conftest.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,12 @@
4747
TestCase("$.'a.b c'", data, [data["a.b c"]]),
4848
TestCase("$['a.b c']", data, [data["a.b c"]]),
4949
TestCase("$..price", data, prices),
50+
TestCase("$.book[1:3]", data, data["book"][1:3]),
51+
TestCase("$.book[1:-1]", data, data["book"][1:-1]),
52+
TestCase("$.book[0:-1:2]", data, data["book"][0:-1:2]),
53+
TestCase("$.book[-1:1]", data, data["book"][-1:1]),
54+
TestCase("$.book[-1:-11:3]", data, data["book"][-1:-11:3]),
55+
TestCase("$.book[:]", data, data["book"][:]),
5056
]
5157
)
5258
def cases(request):

0 commit comments

Comments
 (0)