有時候我們需要對json結構的數據進行更新,或增,或改,或刪。
當json層級比較復雜時操作起來是比較麻煩的,得一層層找下去找到要更新的節點才能操作它。
我用python語言封裝了一個類,提供三個函數分別用於增刪改json的目標節點。
首先我們先確定什么是路徑(path);
如json:
dataA={“code”: 0,
“data”: {“areaCode”: “86”, “avatar”: “”, “countryCode”: “86”, “customer_risk_type”: “0”,
“customer”:{“name”:“syc”,“sex”:“m”}},
“valuelist”:[{“expiration”: 1578043337756, “extendStatusBit”: 1, “firstLogin”: “True”, “id”: 11529},
{“expiration”: 1578043337757, “extendStatusBit”: 2, “firstLogin”: “True”, “id”: 11539,
“transaction”:[{“stockcode”:“601235”},{“price”:2.12}]},
{“expiration”: 1578043337758, “extendStatusBit”: 3, “firstLogin”: “True”, “id”: 11549},
{“expiration”: 1578043337759, “extendStatusBit”: 4, “firstLogin”: “True”, “id”: 11559}],
“msg”: “成功”}
對於以上json,我們需要操作data下customer中的name,則name的路徑為:/data/customer/name;
再如:列表valuelist下第二個元素中transaction列表下的stockcode,則stockcode的路徑為:
/valuelist/(int)1/transaction/(int)0/stockcode
**說明:**由於json允許用字符串類型的數字做key,所以當需要表示列表中第幾個元素時前面加個(int)標識;
1、增
def addkv(self,data,path,key=None,value=None):
"""
for example addkv(dataA,'/data/customer','age',30)
:param data: Target JSON
:param path:Location of new field,as '/data/customer'
:param key: new field
:param value:new field value
:return:Added JSON
1
2
3
4
5
6
7
8
:param data:目標JSON
:param path:目標節點的路徑,如 '/data/customer',/valuelist/(int)1/transaction/(int)0
:param key: 在該節點下新增的鍵
:param value:在該節點下新增的鍵對應的值
:return:新增鍵值完成后的json
1
2
3
4
5
6
注意:如果目標節點不是字典類型程序會報錯,即路徑最后一個節點比如/data/customer中的customer的類型必須是字典;
2、刪
def delete(self,data,path):
"""
Delete the node of the specified path
:param data:Target JSON
:param path:path
:return:JSON after deleting a node
1
2
3
4
5
6
:param data:目標JSON
:param path:路徑
:return:刪除節點后返回的json
1
2
3
3、改
def update(self,data,path,newvalue):
"""
Update JSON
:param data:Target JSON
:param path:Route as '/valuelist/(int)0/expiration'
:param newvalue:Modified value
:return:Updated JSON
1
2
3
4
5
6
7
:param data:目標json
:param path:路徑
:param newvalue:要改為的新值
:return:返回更新后的json
1
2
3
4
完整代碼如下:
#!/usr/bin/env python
#-*- coding:utf-8 -*-
"""
解析json
"""
class rcjson(object):
def resolution(self,local={}):
"""
解析json串中各個字段
:param local:
:return: [{'path': '/code', 'value': 0}, {'path': '/msg', 'value': 'ok'},.........]
"""
def recursive_diff(l,res, path='/'):
delim = '/' if path!= '/' else ''
if(isinstance(l, dict)):
iterl=l.keys()
for k in iterl:
v=l.get(k)
new_path = delim.join([path, k])
recursive_diff(v, res, new_path)
elif(isinstance(l, list)):
enum=enumerate(l, start=0)
for i, item in enum:
new_path = delim.join([path, '(int)'+str(i)])
recursive_diff(item, res, new_path)
else:
res.append({
'path': path,
'value': l
})
result = []
recursive_diff(local, result)
return result
def pathlist(self,paths):
"""
將json節點路徑由/../.../轉變為list
:param paths:[{'path': '/code', 'value': 0}, {'path': '/msg', 'value': 'ok'},.........]
:return:[{'path': ['code'], 'value': 0}, {'path': ['data', 'areaCode'], 'value': '86'}, {'path': ['data', 'avatar'], 'value': ''},.....]
"""
for path in paths:
routes=path.get('path').split("/")[1:]
newroutes=[]
for route in routes:
if('(int)' in route):
start_loc = route.find('(int)')
len_int=len('(int)')
res_str = route[start_loc+len_int:]
newroutes.append(int(res_str))
else:
newroutes.append(route)
path['path']=newroutes
return paths
def updateValue(self,data,path,newvalue):
"""
修改json中指定的字段的值
:param data:目標json
:param path: '/valuelist/(int)0/expiration' 路徑
:param newvalue:修改后的值
:return:[] 或 [{'path': ['code'], 'value': 0}, {'path': ['data', 'areaCode'], 'value': '86'}, {'path': ['data', 'avatar'], 'value': ''},.....]
"""
if(type(data)==dict):
resultStr=self.resolution(data)
index=-1
for pathdic in resultStr:
index=index+1
p=pathdic.get("path")
firstStr=path[:1]
if(firstStr=="/"):
if(p==path):
pathdic["value"]=newvalue
break
else:
if(p=="/"+path):
pathdic["value"] = newvalue
break
if (index + 1 == len(resultStr)):
return [] #沒有找到匹配的路徑
resultList = self.pathlist(resultStr)
return resultList
else:
return []
def composeDict(self,gkey,result,pv,path,start,end):
"""
組裝只有字典的鏈路
:param gkey: globals對象
:param result: 字典
:param pv: 字典 {'path': ['data', 'customer', 'sex'], 'value': 'm'}
:param path: 列表 ['data', 'customer', 'sex']
:param start:
:param end:
:return:
"""
if (self.isExtend(result, path[0]) == False):
gkey[path[0]] = {}
result[path[0]] = gkey[path[0]]
for i in range(start,end):
dict_i1=gkey[path[i - 1]]
pi=path[i]
flag=self.isExtend(dict_i1,pi)
if(flag== False):
gkey[pi] = {}
dict_i1[pi] = gkey[pi]
lastkey = path[end] # 最后一個key
key_1 = path[end-1] # 倒數第二個key
gkey[key_1][lastkey] = pv.get("value")
return result
def composeJSON(self,resultlist):
"""
組裝JSON
:param resultlist: [{'path': ['code'], 'value': 0}, {'path': ['data', 'areaCode'], 'value': '86'}, {'path': ['data', 'avatar'], 'value': ''},......]
:return:json,list也當作json處理先
"""
result={}
gkey=globals()
for pv in resultlist:
path=pv.get('path') #list type ['data', 'areaCode']
if(len(path)==1):
value=pv.get('value')
result[path[0]]=value
elif(len(path)>1):
pathlen = len(path)
self.composeDict(gkey,result,pv,path,1, pathlen - 1)
return result
def dict2list(self,res={}):
"""
之前是列表也按照字典來組裝了,現在按照字符串將列表的地方改為列表
:param result:
:return:最終完整的json
"""
def d2l(result={}):
for key,value in result.items(): #result是第一層
if(type(value)==dict):
keys = value.keys() #value是第二層
k0=list(keys)[0]
if (type(k0) == int): #k0是第三層
kv = []
for k in keys:
v = value.get(k)
kv.append(v)
if(type(v)==dict):
d2l(v)
result[key]=kv
else:
d2l(value)
else:
pass
d2l(res)
return res
def update(self,data,path,newvalue):
"""
Update JSON
:param data:Target JSON
:param path:Route as '/valuelist/(int)0/expiration'
:param newvalue:Modified value
:return:Updated JSON
"""
newpaths=self.updateValue(data,path,newvalue)
if(len(newpaths)==0):
return {}
else:
comJSON = rc.composeJSON(newpaths)
lastJson = rc.dict2list(comJSON)
return lastJson
def addkv(self,data,path,key=None,value=None):
"""
for example addkv(dataA,'/data/customer','age',30)
:param data: Target JSON
:param path:Location of new field,as '/data/customer'
:param key: new field
:param value:new field value
:return:Added JSON
"""
resultStr=self.resolution(data)
kv={}
if(key!=None):
kv['path']=path+'/'+key
else:
kv['path'] = path
kv['value']=value
resultStr.append(kv)
newpaths=self.pathlist(resultStr)
if (len(newpaths) == 0):
return {}
else:
comJSON = rc.composeJSON(newpaths)
lastJson = rc.dict2list(comJSON)
return lastJson
def delete(self,data,path):
"""
Delete the node of the specified path
:param data:Target JSON
:param path:path
:return:JSON after deleting a node
"""
paths=self.resolution(data)
templist=[]
for pathv in paths:
p=pathv.get('path')
index=p.find(path)
if(index==0):
other=p[index+len(path):]
if(len(other)>0):
otherOne=other[0]
if((path ==p) or (otherOne=='/')):
pass
else:
templist.append(pathv)
else:
templist.append(pathv)
newpaths =self.pathlist(templist)
if (len(newpaths) == 0):
return {}
else:
comJSON = rc.composeJSON(newpaths)
lastJson = rc.dict2list(comJSON)
return lastJson
def getKeys(self,data):
keysAll_list = []
def getkeys(data): # Traverse all keys of JSON
if (type(data) == type({})):
keys = data.keys()
for key in keys:
value = data.get(key)
if (type(value) != type({}) and type(value) != type([])):
keysAll_list.append(key)
elif (type(value) == type({})):
keysAll_list.append(key)
getkeys(value)
elif (type(value) == type([])):
keysAll_list.append(key)
for para in value:
if (type(para) == type({}) or type(para) == type([])):
getkeys(para)
else:
keysAll_list.append(para)
getkeys(data)
return keysAll_list
def isExtend(self, ddict={}, tagkey=None): # Check whether the target field tagkey is in data (JSON data)
if (type(ddict) != type({})):
pass
else:
datalen=len(ddict.keys())
if(datalen==0):
pass
else:
key_list = self.getKeys(ddict)
for key in key_list:
if (key == tagkey):
return True
return False
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
驗證
if __name__ == '__main__':
rc=rcjson()
dataA={"code": 0,
"data": {"areaCode": "86", "avatar": "", "countryCode": "86", "customer_risk_type": "0","customer":{"name":"syc","sex":"m"}},
"valuelist":[{"expiration": 1578043337756, "extendStatusBit": 1, "firstLogin": "True", "id": 11529},
{"expiration": 1578043337757, "extendStatusBit": 2, "firstLogin": "True", "id": 11539,"transaction":[{"stockcode":"601235"},{"price":2.12}]},
{"expiration": 1578043337758, "extendStatusBit": 3, "firstLogin": "True", "id": 11549},
{"expiration": 1578043337759, "extendStatusBit": 4, "firstLogin": "True", "id": 11559}
],
"msg": "成功"}
#修改
# result=rc.update(dataA,'/valuelist/(int)1/test01/(int)1/price',10.5)
# print(result)
#新增
result=rc.addkv(dataA,'/data/customer/',key='age',value=30)
print(result)
#刪除
# result=rc.delete(dataA,'/valuelist/(int)1/test01/(int)0/stockcode')
# print(result)