22Author : zhangxianbing1
33Date : 2020-12-27 09:22:14
44LastEditors : zhangxianbing1
5- LastEditTime : 2020-12-31 14:18:23
5+ LastEditTime : 2020-12-31 17:02:04
66Description : JSONPath
77"""
88__version__ = "1.0.0"
2323
2424SEP = ";"
2525# regex patterns
26- REP_PICKUP_QUOTE = re .compile (r"['\" ](.*?)['\" ]" )
26+ REP_PICKUP_QUOTE = re .compile (r"['](.*?)[']" )
2727REP_PICKUP_BRACKET = re .compile (r"[\[](.*?)[\]]" )
2828REP_PUTBACK_QUOTE = re .compile (r"#Q(\d+)" )
2929REP_PUTBACK_BRACKET = re .compile (r"#B(\d+)" )
3232
3333
3434REP_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
3737REP_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
4246def 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)
0 commit comments