指定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
: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)
  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

哈希哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值