有时候我们需要对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
:param data:目标JSON
:param path:目标节点的路径,如 '/data/customer',/valuelist/(int)1/transaction/(int)0
:param key: 在该节点下新增的键
:param value:在该节点下新增的键对应的值
:return:新增键值完成后的json
注意:如果目标节点不是字典类型程序会报错,即路径最后一个节点比如/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
:param data:目标JSON
:param path:路径
:return:删除节点后返回的json
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
:param data:目标json
:param path:路径
:param newvalue:要改为的新值
:return:返回更新后的json
完整代码如下:
#!/usr/bin/env python
#-*- coding:utf-8 -*-
"""
解析json,将json节点和节点的值关联起来
"""
from __future__ import print_function
import unicodedata,json,re
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:
beforeData=''.join(re.findall("\d+",path))
new_path = delim.join([path, '(int)'+beforeData+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)
result[key]=kv
d2l(value)
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
"""
nowpath = []
beforeDatas = re.findall('\d',path)
for i in range(len(beforeDatas)):
num=''
for j in range(i+1):
num=num+beforeDatas[j]
nowpath.append(num)
kv=self.indexNumber(path)
npath=self.updatePath(path,nowpath,kv)
newpaths=self.updateValue(data,npath,newvalue)
if(len(newpaths)==0):
return {}
else:
comJSON = self.composeJSON(newpaths)
lastJson = self.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 = self.composeJSON(newpaths)
lastJson = self.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 = self.composeJSON(newpaths)
lastJson = self.dict2list(comJSON)
return lastJson
def numIndex(self,taglist):
"""
查找列表中数值所在的位置
:param taglist: ['data', 'avatar']
:return:
"""
index=-1
result=[]
for param in taglist:
index=index+1
if(type(param)==int):
result.append(index)
return result
def existNum(self,taglist):
"""
判断taglist中的元素是否有数值类型
:param taglist:
:return:如果有数值则返回该数值在taglist列表中的位置下标,否则返回False
"""
index=-1
for param in taglist:
index=index+1
if(type(param)==int):
return True
if(index+1==len(taglist)):
return False
def checkType(self,res): #path:/data/list/0/stock_id0
"""
将path中各个节点的添加类型
:param path:
:return:[['data', 'dict'], ['list', 'list'], ['0', 'number'], ['aaa', 'list'], ['1', 'number'], ['bbb', 'dict'], ['ccc', 'list'], ['2', 'number'], ['ddd', 'dict'], ['stock_id0', 'object']]
"""
allpath=[]
for i in res:
path=i.get('path')
value=i.get('value')
r=[]
tmp=[]
result_list=[]
result=[]
path=path[1:]
path_list=path.split('/')
l_path=path_list
last_object=path_list[-1]
r.append(last_object)
r.append(value)
result_list.append(r)
tmp.append(last_object)
for i in range(len(path_list)):
r1=[]
r4=[]
if(self.is_number(path_list[i])):
r1.append(path_list[i])
r1.append('dict')
result_list.append(r1)
tmp.append(path_list[i])
r4.append(path_list[i - 1])
r4.append('list')
result_list.append(r4)
tmp.append(path_list[i-1])
for j in path_list:
r2 = []
if j not in tmp:
r2.append(j)
r2.append('dict')
result_list.append(r2)
for m in l_path:
for n in result_list:
if(m==n[0]):
result.append(n)
if(result[-1][1]=='dict'):
result.pop(-1)
allpath.append(result)
return allpath
def is_number(self,s): #判断字符串s是否是数值
try:
float(s)
return True
except Exception as e:
pass
try:
unicodedata.numeric(s)
return True
except Exception as e:
pass
return False
def resolutionJSON(self,res):
"""
将解析好的数据组成单层json
:param res: list
:return:{'/couponType': 2, '/conditionAmount': 10001, '/conditionCurrencyCode': 'HKD', '/submitType': 2}
"""
result={}
for d in res:
key=d.get('path')
value=d.get('value')
result[key]=value
return result
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
def jsonTransliteration(self,jsonStr):
"""
对入参中包含字符串的子json进行转义
:param jsondata:
:return:
"""
if(jsonStr!='NULL' and jsonStr!=None and jsonStr!='None' and jsonStr!='Null' and jsonStr!='null' and jsonStr!='' and jsonStr!=' '):
jsonStr=jsonStr.replace('"{','{').replace('"[{','[{').replace('"{[','{[').replace('"[','[')
jsonStr = jsonStr.replace('}"', '}').replace('}]"', '}]').replace(']}"', ']}').replace(']"', ']')
if('\`' in jsonStr):
jsonStr=jsonStr.replace('\`','')
jsonData=json.loads(jsonStr)
if(self.isExtend(jsonData,'data')):
data=jsonData.get("data")
if(isinstance(data,dict)):
for key,value in data.items():
if(isinstance(value,dict)):
value=str(value).replace('"','\"')
data[key]=value
jsonData["data"]=data
else:
for key,value in jsonData.items():
if(isinstance(value,dict)):
value=str(value).replace('"','\"')
jsonData[key]=value
return jsonData
else:
return json.loads('{}')
def indexNumber(self,path=''):
"""
查找字符串中数值所在串中的位置
:param path:
:return:<class 'list'>: <class 'list'>: [['1', 16], ['2', 35], ['1', 51]]
"""
kv=[]
nums = []
beforeDatas = re.findall('\d', path)
for num in beforeDatas:
indexV=[]
times=path.count(num)
if(times>1):
if(num not in nums):
indexs=re.finditer(num,path)
for index in indexs:
iV = []
i=index.span()[0]
iV.append(num)
iV.append(i)
kv.append(iV)
nums.append(num)
else:
index=path.find(num)
indexV.append(num)
indexV.append(index)
kv.append(indexV)
#根据数字位置排序
indexSort=[]
resultIndex=[]
for vi in kv:
indexSort.append(vi[1])
indexSort.sort()
for i in indexSort:
for v in kv:
if(i==v[1]):
resultIndex.append(v)
return resultIndex
def updatePath(self,path,nowpath=[],kv=[]):
"""
组织新的path
:param path:/valuelist/(int)1/transaction/(int)2/basprice/(int)1
:param nowpath: ['1', '12', '121']
:param kv:[['1', 16], ['2', 35], ['1', 51]]
:return:'/valuelist/(int)1/transaction/(int)12/basprice/(int)121/sdfds'
"""
temp=[]
nowpathlen=len(nowpath)
if(nowpathlen<=1):
return path
for i in range(nowpathlen):
if(i==0):
index0=int(kv[0][1])
p=path[:index0]
temp.append(p)
elif(i==nowpathlen-1):
indexstart = int(kv[i - 1][1]) + len(kv[i - 1][0])
indexend = int(kv[i][1])
p = path[indexstart:indexend]
temp.append(p)
indexlast = int(kv[i][1])
p=path[indexlast + len(kv[i][0]):]
temp.append(p)
else:
indexstart = int(kv[i-1][1])+len(kv[i-1][0])
indexend=int(kv[i][1])
p=path[indexstart:indexend]
temp.append(p)
for i in range(nowpathlen):
temp[i+1]=nowpath[i]+temp[i+1]
result= ''.join(temp)
return result
验证
if __name__ == '__main__':
rc=rcjson()
dataA={"code": 0,
"keylist": [{"expiration": 1578043337756, "extendStatusBit": 1, "firstLogin": "True", "id": 11529},
{"expiration": 1578043337757, "extendStatusBit": 2, "firstLogin": "True", "id": 11539,
"transaction": [{"stockcode": "601235"}, {"stockname": "BABA"},
{"price": 2.12, "basprice": ['1221', '3433']}]},
{"expiration": 1578043337758, "extendStatusBit": 3, "firstLogin": "True", "id": 11549},
{"expiration": 1578043337759, "extendStatusBit": 4, "firstLogin": "True", "id": 11559}
],
"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"},{"stockname":"BABA"},{"price":2.12,"basprice":["1221",'3433']}]},
{"expiration": 1578043337758, "extendStatusBit": 3, "firstLogin": "True", "id": 11549},
{"expiration": 1578043337759, "extendStatusBit": 4, "firstLogin": "True", "id": 11559}
],
"msg": "成功"}
#修改
result=rc.update(dataA,'/valuelist/(int)1/transaction/(int)2/price',12.12)
print(result)
#新增
# result=rc.addkv(dataA,'/valuelist',key='4',value={"expiration": 1578043337759, "extendStatusBit": 4, "firstLogin": "True", "id": 11569})
# print(result)
#删除
# result=rc.delete(dataA,'/valuelist/(int)1/transaction/(int)1')
# print(result)