22Author : zhangxianbing
33Date : 2020-12-27 09:22:14
44LastEditors : zhangxianbing
5- LastEditTime : 2021-03-02 15:56:00
5+ LastEditTime : 2021-03-02 19:53:38
66Description : JSONPath
77"""
88__version__ = "1.0.4"
1212import logging
1313import os
1414import re
15+ import sys
1516from collections import defaultdict
1617from typing import Union
1718
@@ -57,6 +58,8 @@ class JSONPath:
5758 # save special patterns
5859 REP_GET_QUOTE = re .compile (r"['](.*?)[']" )
5960 REP_PUT_QUOTE = re .compile (r"#Q(\d+)" )
61+ REP_GET_BACKQUOTE = re .compile (r"[`](.*?)[`]" )
62+ REP_PUT_BACKQUOTE = re .compile (r"#BQ(\d+)" )
6063 REP_GET_BRACKET = re .compile (r"[\[](.*?)[\]]" )
6164 REP_PUT_BRACKET = re .compile (r"#B(\d+)" )
6265 REP_GET_PAREN = re .compile (r"[\(](.*?)[\)]" )
@@ -70,6 +73,7 @@ class JSONPath:
7073 )
7174
7275 # annotations
76+ f : list
7377 segments : list
7478 lpath : int
7579 subx = defaultdict (list )
@@ -82,6 +86,8 @@ def __init__(self, expr: str):
8286 self .lpath = len (self .segments )
8387 logger .debug (f"segments : { self .segments } " )
8488
89+ self .caller_globals = sys ._getframe (1 ).f_globals
90+
8591 def parse (self , obj , result_type = "VALUE" ):
8692 if not isinstance (obj , (list , dict )):
8793 raise TypeError ("obj must be a list or a dict." )
@@ -99,21 +105,26 @@ def parse(self, obj, result_type="VALUE"):
99105
100106 def _parse_expr (self , expr ):
101107 logger .debug (f"before expr : { expr } " )
102-
108+ # pick up special patterns
103109 expr = JSONPath .REP_GET_QUOTE .sub (self ._get_quote , expr )
110+ expr = JSONPath .REP_GET_BACKQUOTE .sub (self ._get_backquote , expr )
104111 expr = JSONPath .REP_GET_BRACKET .sub (self ._get_bracket , expr )
105112 expr = JSONPath .REP_GET_PAREN .sub (self ._get_paren , expr )
113+ # split
106114 expr = JSONPath .REP_DOUBLEDOT .sub (f"{ JSONPath .SEP } ..{ JSONPath .SEP } " , expr )
107115 expr = JSONPath .REP_DOT .sub (JSONPath .SEP , expr )
116+ # put back
108117 expr = JSONPath .REP_PUT_PAREN .sub (self ._put_paren , expr )
109118 expr = JSONPath .REP_PUT_BRACKET .sub (self ._put_bracket , expr )
119+ expr = JSONPath .REP_PUT_BACKQUOTE .sub (self ._put_backquote , expr )
110120 expr = JSONPath .REP_PUT_QUOTE .sub (self ._put_quote , expr )
111121 if expr .startswith ("$;" ):
112122 expr = expr [2 :]
113123
114124 logger .debug (f"after expr : { expr } " )
115125 return expr
116126
127+ # TODO abstract get and put procedures
117128 def _get_quote (self , m ):
118129 n = len (self .subx ["#Q" ])
119130 self .subx ["#Q" ].append (m .group (1 ))
@@ -122,6 +133,14 @@ def _get_quote(self, m):
122133 def _put_quote (self , m ):
123134 return self .subx ["#Q" ][int (m .group (1 ))]
124135
136+ def _get_backquote (self , m ):
137+ n = len (self .subx ["#BQ" ])
138+ self .subx ["#BQ" ].append (m .group (1 ))
139+ return f"`#BQ{ n } `"
140+
141+ def _put_backquote (self , m ):
142+ return self .subx ["#BQ" ][int (m .group (1 ))]
143+
125144 def _get_bracket (self , m ):
126145 n = len (self .subx ["#B" ])
127146 self .subx ["#B" ].append (m .group (1 ))
@@ -139,7 +158,7 @@ def _put_paren(self, m):
139158 return self .subx ["#P" ][int (m .group (1 ))]
140159
141160 @staticmethod
142- def _f_brackets (m ):
161+ def _gen_obj (m ):
143162 ret = "__obj"
144163 for e in m .group (1 ).split ("." ):
145164 ret += '["%s"]' % e
@@ -155,26 +174,39 @@ def _traverse(f, obj, i: int, path: str, *args):
155174 f (v , i , f"{ path } { JSONPath .SEP } { k } " , * args )
156175
157176 @staticmethod
158- def _getattr (obj : dict , path : str ):
177+ def _getattr (obj : dict , path : str , * , convert_number_str = False ):
159178 r = obj
160179 for k in path .split ("." ):
161180 try :
162181 r = r .get (k )
163182 except (AttributeError , KeyError ) as err :
164183 logger .error (err )
165184 return None
166-
185+ if convert_number_str and isinstance (r , str ):
186+ try :
187+ if r .isdigit ():
188+ return int (r )
189+ return float (r )
190+ except ValueError :
191+ pass
167192 return r
168193
169194 @staticmethod
170195 def _sorter (obj , sortbys ):
171196 for sortby in sortbys .split ("," )[::- 1 ]:
172197 if sortby .startswith ("~" ):
173198 obj .sort (
174- key = lambda t , k = sortby : JSONPath ._getattr (t [1 ], k [1 :]), reverse = True
199+ key = lambda t , k = sortby : JSONPath ._getattr (
200+ t [1 ], k [1 :], convert_number_str = True
201+ ),
202+ reverse = True ,
175203 )
176204 else :
177- obj .sort (key = lambda t , k = sortby : JSONPath ._getattr (t [1 ], k ))
205+ obj .sort (
206+ key = lambda t , k = sortby : JSONPath ._getattr (
207+ t [1 ], k , convert_number_str = True
208+ )
209+ )
178210
179211 def _filter (self , obj , i : int , path : str , step : str ):
180212 r = False
@@ -245,7 +277,7 @@ def _trace(self, obj, i: int, path):
245277 # filter
246278 if step .startswith ("?(" ) and step .endswith (")" ):
247279 step = step [2 :- 1 ]
248- step = JSONPath .REP_FILTER_CONTENT .sub (self ._f_brackets , step )
280+ step = JSONPath .REP_FILTER_CONTENT .sub (self ._gen_obj , step )
249281 self ._traverse (self ._filter , obj , i + 1 , path , step )
250282 return
251283
0 commit comments