指定json的某个节点进行增、删、改

有时候我们需要对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)

posted on 2020-03-04 10:03  哈希哥  阅读(3825)  评论(0编辑  收藏  举报