22Author : zhangxianbing1
33Date : 2020-12-27 09:22:14
44LastEditors : zhangxianbing1
5- LastEditTime : 2021-01-04 11:01:24
5+ LastEditTime : 2021-01-04 12:40:59
66Description : JSONPath
77"""
88__version__ = "0.0.3"
99__author__ = "zhangxianbing"
1010
1111import json
1212import re
13- from typing import Union
1413from collections import defaultdict
14+ from typing import Union
1515
1616RESULT_TYPE = {
1717 "VALUE" : "A list of specific values." ,
3939# pylint: disable=invalid-name,missing-function-docstring,missing-class-docstring,eval-used
4040
4141
42- def concat (x , y , con = SEP ):
43- return f"{ x } { con } { y } "
44-
45-
4642def _getattr (obj : dict , path : str ):
4743 r = obj
4844 for k in path .split ("." ):
@@ -61,18 +57,13 @@ class ExprSyntaxError(Exception):
6157
6258class JSONPath :
6359 # annotations
64- result : Union [list , dict ]
65- result_type : str
66- subx = defaultdict (list )
6760 steps : list
6861 lpath : int
62+ subx = defaultdict (list )
63+ result : Union [list , dict ]
64+ result_type : str
6965
70- def __init__ (self , expr : str , * , result_type = "VALUE" ):
71- if result_type not in RESULT_TYPE :
72- raise ValueError (f"result_type must be one of { tuple (RESULT_TYPE .keys ())} " )
73- self .result_type = result_type
74-
75- # parse expression
66+ def __init__ (self , expr : str ):
7667 expr = self ._parse_expr (expr )
7768 self .steps = expr .split (SEP )
7869 self .lpath = len (self .steps )
@@ -118,38 +109,40 @@ def _f_brackets(m):
118109 ret += '["%s"]' % e
119110 return ret
120111
121- def parse (self , obj ):
112+ def parse (self , obj , result_type = "VALUE" ):
122113 if not isinstance (obj , (list , dict )):
123114 raise TypeError ("obj must be a list or a dict." )
124-
115+ if result_type not in RESULT_TYPE :
116+ raise ValueError (f"result_type must be one of { tuple (RESULT_TYPE .keys ())} " )
117+ self .result_type = result_type
125118 if self .result_type == "FIELD" :
126119 self .result = {}
127120 else :
128121 self .result = []
129122
130- self ._trace (obj , 0 )
123+ self ._trace (obj , 0 , "$" )
131124
132125 return self .result
133126
134127 @staticmethod
135- def _traverse (f , obj , i : int , * args ):
128+ def _traverse (f , obj , i : int , path : str , * args ):
136129 if isinstance (obj , list ):
137- for v in obj :
138- f (v , i , * args )
130+ for idx , v in enumerate ( obj ) :
131+ f (v , i , f" { path } { SEP } { idx } " , * args )
139132 elif isinstance (obj , dict ):
140- for v in obj .values ():
141- f (v , i , * args )
133+ for k , v in obj .items ():
134+ f (v , i , f" { path } { SEP } { k } " , * args )
142135
143- def _filter (self , obj , i : int , step : str ):
136+ def _filter (self , obj , i : int , path : str , step : str ):
144137 r = False
145138 try :
146139 r = eval (step , None , {"__obj" : obj })
147140 except Exception as err :
148141 print (err )
149142 if r :
150- self ._trace (obj , i )
143+ self ._trace (obj , i , path )
151144
152- def _trace (self , obj , i : int ):
145+ def _trace (self , obj , i : int , path ):
153146 """Perform operation on object.
154147
155148 Args:
@@ -159,54 +152,59 @@ def _trace(self, obj, i: int):
159152
160153 # store
161154 if i >= self .lpath :
162- self .result .append (obj )
155+ if self .result_type == "VALUE" :
156+ self .result .append (obj )
157+ elif self .result_type == "PATH" :
158+ self .result .append (path )
159+ elif self .result_type == "FIELD" :
160+ pass
163161 print (obj )
164162 return
165163
166164 step = self .steps [i ]
167165
168166 # wildcard
169167 if step == "*" :
170- self ._traverse (self ._trace , obj , i + 1 )
168+ self ._traverse (self ._trace , obj , i + 1 , path )
171169 return
172170
173171 # recursive descent
174172 if step == ".." :
175- self ._trace (obj , i + 1 )
176- self ._traverse (self ._trace , obj , i )
173+ self ._trace (obj , i + 1 , path )
174+ self ._traverse (self ._trace , obj , i , path )
177175 return
178176
179177 # get value from list
180178 if isinstance (obj , list ) and step .isdigit ():
181179 ikey = int (step )
182180 if ikey < len (obj ):
183- self ._trace (obj [ikey ], i + 1 )
181+ self ._trace (obj [ikey ], i + 1 , f" { path } { SEP } { step } " )
184182 return
185183
186184 # get value from dict
187185 if isinstance (obj , dict ) and step in obj :
188- self ._trace (obj [step ], i + 1 )
186+ self ._trace (obj [step ], i + 1 , f" { path } { SEP } { step } " )
189187 return
190188
191189 # slice
192190 if isinstance (obj , list ) and REP_SLICE_CONTENT .fullmatch (step ):
193191 vals = eval (f"obj[{ step } ]" )
194- for v in vals :
195- self ._trace (v , i + 1 )
192+ for idx , v in enumerate ( vals ) :
193+ self ._trace (v , i + 1 , f" { path } { SEP } { idx } " )
196194 return
197195
198196 # select
199197 if isinstance (obj , dict ) and REP_SELECT_CONTENT .fullmatch (step ):
200198 for k in step .split ("," ):
201199 if k in obj :
202- self ._trace (obj [k ], i + 1 )
200+ self ._trace (obj [k ], i + 1 , f" { path } { SEP } { k } " )
203201 return
204202
205203 # filter
206204 if step .startswith ("?(" ) and step .endswith (")" ):
207205 step = step [2 :- 1 ]
208206 step = REP_FILTER_CONTENT .sub (self ._f_brackets , step )
209- self ._traverse (self ._filter , obj , i + 1 , step )
207+ self ._traverse (self ._filter , obj , i + 1 , path , step )
210208 return
211209
212210 # sort
@@ -229,11 +227,12 @@ def _trace(self, obj, i: int):
229227 else :
230228 obj .sort (key = lambda t , k = sortby : _getattr (t [1 ], k ))
231229 obj = {k : v for k , v in obj }
232- self ._traverse (self ._trace , obj , i + 1 )
230+ self ._traverse (self ._trace , obj , i + 1 , path )
233231 return
234232
235233
236234if __name__ == "__main__" :
237235 with open ("test/data/2.json" , "rb" ) as f :
238236 d = json .load (f )
239- JSONPath ("$.scores[/(score)].score" ).parse (d )
237+ D = JSONPath ("$.scores[/(score)].score" ).parse (d , "PATH" )
238+ print (D )
0 commit comments