配置文件(JSON格式)替换工具(添加增加键值功能)

2019-05-19  本文已影响0人  西瓜雪梨桔子汁

实际使用中发现,在进行配置时,对增加键值的需要还是很旺盛的,比如一个账号列表配置要新增账号、一个配置JSON Array增加完整的JSON配置块等等,鉴于此,更新了工具,满足此类需求。

1.分析

增加配置涉及的类型基本有这么几种:

[
            {
                "productId":"commonProduct_001",
                "productName":"矿泉水",
                "productPrice":"2.00"
            },
            {
                "productId":"commonProduct_002",
                "productName":"冰可乐",
                "productPrice":"3.50"
            }
        ]

前2种很好解决,对一个JSON对象判断键在不在,如果在就是替换、不在就是增加,且增加都是字符串类型,直接替换原值就行,后面2种就显得麻烦些。

2.实现

在之前文章实现的基础上,重点改造点主要有以下几方面:

2.1 检测字符串是否为JSON

基本原理是利用json.loads()尝试解析字符串,如果不是合法json格式,则会抛出ValueError异常。

def is_json(json_str):
    '''
    判读是否合法的JSON字符串
    '''
    try:
        json.loads(json_str)
    except ValueError:
        return False
    return True

测试的结果如下:


image.png

2.2 替换/增加字符串逻辑

涉及代码片段:

    def json_replace_object(self, conf, key, val):
        '''
        对json的指定key替换值
        ''' 
        if(not conf.has_key(key)):
            print(u'增加key为:%s、val为:%s键值对' % (key, val))
        # 增加或替换值
        if(is_json(val)):
            # 增加的是json类型的值
            conf[key] = json.loads(val, object_pairs_hook=collections.OrderedDict)
            #print conf[key]
        else:
            # 增加的是str类型的值
            conf[key] = val
        # 返回
        return conf      

处理逻辑:

2.3 替换或增加JSON Array字符串元素

涉及代码片段:

  def json_replace_array(self, conf_arrary, index, val):
        '''
        Json Array替换值
        ''' 
        if(len(conf_arrary) <= index):
            print(u'增加:%s到%s中' % (val, conf_arrary))
            # 增加
            if(is_json(val)):
                # 增加的是json类型的值
                conf_arrary.insert(index,json.loads(val, object_pairs_hook=collections.OrderedDict))
            else:
                # 增加的是str类型的值
                conf_arrary.insert(index,val)
        else:
            #替换值
            print(u'将%s的第%s个元素替换为:%s' % (conf_arrary, index, val))
            conf_arrary[index] = val
        # 返回
        return conf_arrary

对JSON Array处理,关注参数index,它表示需要替换或者增加的JSON Array索引值,所以大致逻辑是:

2.4 JSON Array的子JSON Object新增或替换

这种事比较复杂的一种情形,举几个例子:

{
    "productId": "modifiedSpecialityProduct_001",
    "productName": "椰子糖(无糖型)",
    "productPrice": "30.00",
    "addKey": "addKey-val"
},
{
    "productId": "specialityProduct_002",
    "productName": "芒果干",
    "productPrice": "35.00"
}
"productList": [
            {
                "productId": "modifiedSpecialityProduct_001",
                "productName": "椰子糖(无糖型)",
                "productPrice": "30.00"
            },
            {
                "productId": "specialityProduct_002",
                "productName": "芒果干",
                "productPrice": "35.00"
            },
            {
                "productId": "specialityProduct_002",
                "productName": "榴莲糖",
                "productPrice": "35.00"
            }
        ]

相关的代码片段:

        if(len(key_pattern.split('.')) == 1):
            if(not '#' in key_pattern):
                return self.json_replace_object(conf, key_pattern, val)     
            else:
               real_key = key_pattern.split('#')[0]
               index = int(key_pattern.split('#')[1])
               conf_arrary = conf[real_key]
               replaced_array = self.json_replace_array(conf_arrary, index, val)
               conf[real_key] = replaced_array
               return conf
        else:
            key = key_pattern.split('.')[0]
            if '#' in key:
                # 剔除#index拿到key
                real_key = key.split('#')[0]
                # 从#index拿到array的index
                index = int(key.split('#')[1])
                # 先取的array,在从array中按照index取出需要的
                conf_arrary = conf[real_key]
                if(len(conf_arrary) <= index):
                    # 从第0个copy
                    conf_arrary.insert(index,conf_arrary[0])
                    real_conf = conf_arrary[index]
                else:
                    real_conf = conf_arrary[index]
                # 对待替换的配置继续递归处理
                replaced_conf = self.json_replace_recursive(real_conf, key_pattern[key_pattern.index('.')+1:], val)
                # 修改好的值替换掉原本的这个index的array中的值
                if(len(conf_arrary) <= index):
                    conf_arrary.insert(index,replaced_conf)
                else:
                    conf_arrary[index] = replaced_conf
                # 再将这个array赋值回原本json的这个key的部分,达到改变配置效果
                conf[real_key] = conf_arrary
                # 返回调用者的是对原始json替换后的
                return conf

相关逻辑都在判断key按照“.”拆分后能拆分列表大小,只能拆分为1说明到达目标key那一级,反之需要递归处理,所以逻辑都在else处理的递归逻辑中。
按照之前定义,只有“#”指定一个JSON Array的索引值,对解析到key包含“#”,我们就知道是要进行元素替换或者新增了。
跟上面类似,这里逻辑是:

这里这个copy操作是有bug的,如果本身是空的array,就会出问题,copy不到值,且递归时检测是否含有子key也会出错。

2.5 完整替换逻辑

如下是替换或新增的逻辑:

def is_json(json_str):
    '''
    判读是否合法的JSON字符串
    '''
    try:
        json.loads(json_str)
    except ValueError:
        return False
    return True

class ContentModifier(object):
    '''
    配置内容修改器,依据配置项的key-val对,进行配置文件的修改
    '''
    def __init__(self, conf_paraser):
        '''
        初始化方法
        '''
        self.conf_paraser = conf_paraser       
        
    def json_replace_object(self, conf, key, val):
        '''
        对json的指定key替换值
        ''' 
        if(not conf.has_key(key)):
            print(u'增加key为:%s、val为:%s键值对' % (key, val))
        # 增加或替换值
        if(is_json(val)):
            # 增加的是json类型的值
            conf[key] = json.loads(val, object_pairs_hook=collections.OrderedDict)
            #print conf[key]
        else:
            # 增加的是str类型的值
            conf[key] = val
        # 返回
        return conf      
        
    def json_replace_array(self, conf_arrary, index, val):
        '''
        Json Array替换值
        ''' 
        if(len(conf_arrary) <= index):
            print(u'增加:%s到%s中' % (val, conf_arrary))
            # 增加
            if(is_json(val)):
                # 增加的是json类型的值
                conf_arrary.insert(index,json.loads(val, object_pairs_hook=collections.OrderedDict))
            else:
                # 增加的是str类型的值
                conf_arrary.insert(index,val)
        else:
            #替换值
            print(u'将%s的第%s个元素替换为:%s' % (conf_arrary, index, val))
            conf_arrary[index] = val
        # 返回
        return conf_arrary
                
    def json_replace_recursive(self, conf, key_pattern, val):
        '''
        按照key_pattern递归到最后一层,将其值修改为传入的val
        以CsvFileExportToCoreService#0.exportRules#0.fileExportRules.rule为例,表示:
            待修改的值在一级keyCsvFileExportToCoreService的值中,且它是array,#0指明要修改的在array的第一个
            待修改的值在第一个array的key为exportRules中,这个exportRules的值也是array,#0需要修改的指明要修改的在array的第一个
            待修改的值在第一个array的fileExportRules指定值中,此为json对象
            待修改的值在json对象的rule中
        '''
        print '-------%s : %s' % (key_pattern, val)
        if(len(key_pattern.split('.')) == 1):
            if(not '#' in key_pattern):
                return self.json_replace_object(conf, key_pattern, val)     
            else:
               real_key = key_pattern.split('#')[0]
               index = int(key_pattern.split('#')[1])
               conf_arrary = conf[real_key]
               replaced_array = self.json_replace_array(conf_arrary, index, val)
               conf[real_key] = replaced_array
               return conf
        else:
            key = key_pattern.split('.')[0]
            if '#' in key:
                # 剔除#index拿到key
                real_key = key.split('#')[0]
                # 从#index拿到array的index
                index = int(key.split('#')[1])
                # 先取的array,在从array中按照index取出需要的
                conf_arrary = conf[real_key]
                if(len(conf_arrary) <= index):
                    # 从第0个copy
                    conf_arrary.insert(index,conf_arrary[0])
                    real_conf = conf_arrary[index]
                else:
                    real_conf = conf_arrary[index]
                # 对待替换的配置继续递归处理
                replaced_conf = self.json_replace_recursive(real_conf, key_pattern[key_pattern.index('.')+1:], val)
                # 修改好的值替换掉原本的这个index的array中的值
                if(len(conf_arrary) <= index):
                    conf_arrary.insert(index,replaced_conf)
                else:
                    conf_arrary[index] = replaced_conf
                # 再将这个array赋值回原本json的这个key的部分,达到改变配置效果
                conf[real_key] = conf_arrary
                # 返回调用者的是对原始json替换后的
                return conf
            else:
                # 不是array类型,直接取出值进行递归替换
                # print '========== ' + key_pattern[key_pattern.index('.')+1:]
                replaced_conf = self.json_replace_recursive(conf[key], key_pattern[key_pattern.index('.')+1:], val)
                # 修改好的json替换原始json
                conf[key] = replaced_conf
                # 返回替换后的原始json
                return conf
            
    def json_modify(self, section, content):
        '''
        按照配置conf,取出其section段配置,对content进行修改
        '''
        #print content
        replaced_json = content
        if(not self.conf_paraser.exist_section(section)):
            raise RuntimeError(u'配置文件:%s没有section名为:%s的配置' % (self.conf_paraser.path, section))
        else:
            items = self.conf_paraser.get_section_items(section)
            # 替换所有需要的项
            for item in items:
                print '%s : %s' % (item[0], item[1])
                replaced_json = self.json_replace_recursive(replaced_json, item[0], item[1])
        # 返回修改好的配置json
        return replaced_json

3.测试

测试数据:命名为 data.config

# 注释行,将被忽略
####################################################################
##   注释
####################################################################
{
    "commonProduct":{
        "name":"普通商品汇总",
        "productList":[
            {
                "productId":"commonProduct_001",
                "productName":"矿泉水",
                "productPrice":"2.00"
            },
            {
                "productId":"commonProduct_002",
                "productName":"冰可乐",
                "productPrice":"3.50"
            }
        ]
    },
    "specialityProduct":{
        "name":"特色商品汇总",
        "productList":[
            {
                "productId":"specialityProduct_001",
                "productName":"椰子糖",
                "productPrice":"30.00"
            },
            {
                "productId":"specialityProduct_002",
                "productName":"芒果干",
                "productPrice":"35.00"
            }
        ]
    },
    "arryTest":["001"]
}

配置文件:命名为conf.ini

[data.config]
;price
commonProduct.desc=我是加入测试的
commonProduct.productList#0.productPrice=3.00
commonProduct.productList#1.productPrice=2.50

;id
specialityProduct.productList#0.productId=modifiedSpecialityProduct_001
;name
specialityProduct.productList#0.productName=椰子糖(无糖型)
;json arr modify and add
arryTest#0=modify_001
arryTest#1=add_001

; add key and val
specialityProduct.productList#0.addKey=addKey-val

; add whole object
;specialityProduct.productList#2.productId=addedSpecialityProduct_001
;specialityProduct.productList#2.productName=椰子糖(含糖型)
;specialityProduct.productList#2.productPrice=30.00
;specialityProduct.productList#2.additionalKey=additionalKey-val

specialityProduct.productList#3={"productId":"specialityProduct_002","productName":"榴莲糖","productPrice":"35.00"}

; add object 
objectTest={"name" : "ACME","shares" : 100,"price" : 542.23}

测试操作:


image.png

处理完成的文件:

# 注释行,将被忽略
####################################################################
##   注释
####################################################################
{
    "commonProduct": {
        "name": "普通商品汇总",
        "productList": [
            {
                "productId": "commonProduct_001",
                "productName": "矿泉水",
                "productPrice": 3.0
            },
            {
                "productId": "commonProduct_002",
                "productName": "冰可乐",
                "productPrice": 2.5
            }
        ],
        "desc": "我是加入测试的"
    },
    "specialityProduct": {
        "name": "特色商品汇总",
        "productList": [
            {
                "productId": "modifiedSpecialityProduct_001",
                "productName": "椰子糖(无糖型)",
                "productPrice": "30.00",
                "addKey": "addKey-val"
            },
            {
                "productId": "specialityProduct_002",
                "productName": "芒果干",
                "productPrice": "35.00"
            },
            {
                "productId": "specialityProduct_002",
                "productName": "榴莲糖",
                "productPrice": "35.00"
            }
        ]
    },
    "arryTest": [
        "modify_001",
        "add_001"
    ],
    "objectTest": {
        "name": "ACME",
        "shares": 100,
        "price": 542.23
    }
}

4. 总结

上一篇下一篇

猜你喜欢

热点阅读