22Author : zhangxianbing1
33Date : 2020-12-27 09:22:14
44LastEditors : zhangxianbing1
5- LastEditTime : 2020-12-31 11:07:08
5+ LastEditTime : 2020-12-31 14:18:23
66Description : JSONPath
77"""
88__version__ = "1.0.0"
99__author__ = "zhangxianbing"
1010
1111import json
12- import logging
13- import os
1412import re
1513import sys
1614from typing import Any , Dict , Iterable
2927REP_PICKUP_BRACKET = re .compile (r"[\[](.*?)[\]]" )
3028REP_PUTBACK_QUOTE = re .compile (r"#Q(\d+)" )
3129REP_PUTBACK_BRACKET = re .compile (r"#B(\d+)" )
32- REP_DOUBLEDOTS = re .compile (r"\.\." )
30+ REP_DOUBLEDOT = re .compile (r"\.\." )
3331REP_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
174187if __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)
0 commit comments