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

實(shí)際使用中發(fā)現(xiàn),在進(jìn)行配置時(shí)霍弹,對(duì)增加鍵值的需要還是很旺盛的,比如一個(gè)賬號(hào)列表配置要新增賬號(hào)光涂、一個(gè)配置JSON Array增加完整的JSON配置塊等等庞萍,鑒于此拧烦,更新了工具忘闻,滿足此類需求。

1.分析

增加配置涉及的類型基本有這么幾種:

  • JSON Object增加新的鍵值對(duì)恋博,值為普通字符串
  • JSON Array內(nèi)部是一個(gè)個(gè)字符串齐佳,如:['u1001','U1002']
  • JSON Object增加新的鍵值對(duì),值是復(fù)雜的JSON Obect或者JSON Array
  • JSON Array內(nèi)部是一個(gè)個(gè)JSON Object债沮,如:
[
            {
                "productId":"commonProduct_001",
                "productName":"礦泉水",
                "productPrice":"2.00"
            },
            {
                "productId":"commonProduct_002",
                "productName":"冰可樂",
                "productPrice":"3.50"
            }
        ]

前2種很好解決炼吴,對(duì)一個(gè)JSON對(duì)象判斷鍵在不在,如果在就是替換疫衩、不在就是增加硅蹦,且增加都是字符串類型,直接替換原值就行闷煤,后面2種就顯得麻煩些童芹。

2.實(shí)現(xiàn)

在之前文章實(shí)現(xiàn)的基礎(chǔ)上,重點(diǎn)改造點(diǎn)主要有以下幾方面:

  • 對(duì)替換JSON Object部分改造鲤拿,替換某個(gè)key前需要先判斷它在不在當(dāng)前JSON中
  • 對(duì)替換JSON Array部分改造假褪,替換某個(gè)index指向的array時(shí)先判斷array的長度是否大于index,如果大于則是替換近顷,反之是增加生音。
  • 增加邏輯中對(duì)待增加的值進(jìn)行JSON合法性檢測(cè),是非法的JSON結(jié)構(gòu)以普通字符串添加窒升,否則以解析完成的字典格式添加缀遍。

2.1 檢測(cè)字符串是否為JSON

基本原理是利用json.loads()嘗試解析字符串,如果不是合法json格式饱须,則會(huì)拋出ValueError異常域醇。

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

測(cè)試的結(jié)果如下:


image.png

2.2 替換/增加字符串邏輯

涉及代碼片段:

    def json_replace_object(self, conf, key, val):
        '''
        對(duì)json的指定key替換值
        ''' 
        if(not conf.has_key(key)):
            print(u'增加key為:%s、val為:%s鍵值對(duì)' % (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      

處理邏輯:

  • 檢測(cè)JSON/字典是否含有指定的key,沒有則添加歹苦、有則替換青伤。不過對(duì)于python添加、替換操作可合二為一殴瘦。
  • 判斷待參數(shù)val是否為合法的JSON字符串狠角,是則轉(zhuǎn)為JSON對(duì)象在添加或替換,否則作為原始字符串添加或替換蚪腋。

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個(gè)元素替換為:%s' % (conf_arrary, index, val))
            conf_arrary[index] = val
        # 返回
        return conf_arrary

對(duì)JSON Array處理丰歌,關(guān)注參數(shù)index,它表示需要替換或者增加的JSON Array索引值屉凯,所以大致邏輯是:

  • 如果索引值index小于JSON Array長度立帖,只是替換,且替換的值為普通字符串(暫時(shí)不支持這樣替換JSON Array中完整的JSON對(duì)象的操作悠砚,如果需要這里也是判斷參數(shù)值是否為JSON字符串就可以)
  • 如果索引值index大于或等于JSON Array長度晓勇,就是需要增加array元素的情況了,在依據(jù)值是否為合法JSON字符串處理:
    • 是普通字符串灌旧,以字符串方式增加
    • 是合法JSON字符串绑咱,解析為字典,添加到目標(biāo)字典中

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

這種事比較復(fù)雜的一種情形枢泰,舉幾個(gè)例子:

  • 新增普通鍵值對(duì)
    假設(shè)文章開頭JSON例子要在JSON Array的第一個(gè)元素新增描融,形成下面的JSON:
{
    "productId": "modifiedSpecialityProduct_001",
    "productName": "椰子糖(無糖型)",
    "productPrice": "30.00",
    "addKey": "addKey-val"
},
{
    "productId": "specialityProduct_002",
    "productName": "芒果干",
    "productPrice": "35.00"
}
  • 新增JSON Array元素
"productList": [
            {
                "productId": "modifiedSpecialityProduct_001",
                "productName": "椰子糖(無糖型)",
                "productPrice": "30.00"
            },
            {
                "productId": "specialityProduct_002",
                "productName": "芒果干",
                "productPrice": "35.00"
            },
            {
                "productId": "specialityProduct_002",
                "productName": "榴蓮糖",
                "productPrice": "35.00"
            }
        ]

相關(guān)的代碼片段:

        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個(gè)copy
                    conf_arrary.insert(index,conf_arrary[0])
                    real_conf = conf_arrary[index]
                else:
                    real_conf = conf_arrary[index]
                # 對(duì)待替換的配置繼續(xù)遞歸處理
                replaced_conf = self.json_replace_recursive(real_conf, key_pattern[key_pattern.index('.')+1:], val)
                # 修改好的值替換掉原本的這個(gè)index的array中的值
                if(len(conf_arrary) <= index):
                    conf_arrary.insert(index,replaced_conf)
                else:
                    conf_arrary[index] = replaced_conf
                # 再將這個(gè)array賦值回原本json的這個(gè)key的部分衡蚂,達(dá)到改變配置效果
                conf[real_key] = conf_arrary
                # 返回調(diào)用者的是對(duì)原始json替換后的
                return conf

相關(guān)邏輯都在判斷key按照“.”拆分后能拆分列表大小窿克,只能拆分為1說明到達(dá)目標(biāo)key那一級(jí),反之需要遞歸處理毛甲,所以邏輯都在else處理的遞歸邏輯中年叮。
按照之前定義,只有“#”指定一個(gè)JSON Array的索引值丽啡,對(duì)解析到key包含“#”谋右,我們就知道是要進(jìn)行元素替換或者新增了。
跟上面類似补箍,這里邏輯是:

  • 索引值index小于array長度改执,替換操作,只是需要遞歸替換坑雅,因?yàn)閗ey還含有“.”
  • 索引值index大于或等于array長度辈挂,增加操作:先把Array的地1個(gè)部分copy到array的index位置,再作為參數(shù)交給遞歸方法處理裹粤;處理完的Array终蒂,在賦值回原始的array對(duì)應(yīng)的key,實(shí)現(xiàn)替換。

這里這個(gè)copy操作是有bug的拇泣,如果本身是空的array噪叙,就會(huì)出問題,copy不到值霉翔,且遞歸時(shí)檢測(cè)是否含有子key也會(huì)出錯(cuò)睁蕾。

2.5 完整替換邏輯

如下是替換或新增的邏輯:

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

class ContentModifier(object):
    '''
    配置內(nèi)容修改器,依據(jù)配置項(xiàng)的key-val對(duì)债朵,進(jìn)行配置文件的修改
    '''
    def __init__(self, conf_paraser):
        '''
        初始化方法
        '''
        self.conf_paraser = conf_paraser       
        
    def json_replace_object(self, conf, key, val):
        '''
        對(duì)json的指定key替換值
        ''' 
        if(not conf.has_key(key)):
            print(u'增加key為:%s子眶、val為:%s鍵值對(duì)' % (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個(gè)元素替換為:%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為例序芦,表示:
            待修改的值在一級(jí)keyCsvFileExportToCoreService的值中臭杰,且它是array,#0指明要修改的在array的第一個(gè)
            待修改的值在第一個(gè)array的key為exportRules中谚中,這個(gè)exportRules的值也是array渴杆,#0需要修改的指明要修改的在array的第一個(gè)
            待修改的值在第一個(gè)array的fileExportRules指定值中,此為json對(duì)象
            待修改的值在json對(duì)象的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個(gè)copy
                    conf_arrary.insert(index,conf_arrary[0])
                    real_conf = conf_arrary[index]
                else:
                    real_conf = conf_arrary[index]
                # 對(duì)待替換的配置繼續(xù)遞歸處理
                replaced_conf = self.json_replace_recursive(real_conf, key_pattern[key_pattern.index('.')+1:], val)
                # 修改好的值替換掉原本的這個(gè)index的array中的值
                if(len(conf_arrary) <= index):
                    conf_arrary.insert(index,replaced_conf)
                else:
                    conf_arrary[index] = replaced_conf
                # 再將這個(gè)array賦值回原本json的這個(gè)key的部分将塑,達(dá)到改變配置效果
                conf[real_key] = conf_arrary
                # 返回調(diào)用者的是對(duì)原始json替換后的
                return conf
            else:
                # 不是array類型,直接取出值進(jìn)行遞歸替換
                # 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段配置,對(duì)content進(jìn)行修改
        '''
        #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)
            # 替換所有需要的項(xiàng)
            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.測(cè)試

測(cè)試數(shù)據(jù):命名為 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=我是加入測(cè)試的
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}

測(cè)試操作:


image.png

處理完成的文件:

# 注釋行来吩,將被忽略
####################################################################
##   注釋
####################################################################
{
    "commonProduct": {
        "name": "普通商品匯總",
        "productList": [
            {
                "productId": "commonProduct_001",
                "productName": "礦泉水",
                "productPrice": 3.0
            },
            {
                "productId": "commonProduct_002",
                "productName": "冰可樂",
                "productPrice": 2.5
            }
        ],
        "desc": "我是加入測(cè)試的"
    },
    "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. 總結(jié)

  • 未能實(shí)現(xiàn)移除已有鍵值對(duì)
  • 暫未對(duì)空J(rèn)SON Arry增加JSON Object的異常情況處理
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市蔽莱,隨后出現(xiàn)的幾起案子弟疆,更是在濱河造成了極大的恐慌,老刑警劉巖盗冷,帶你破解...
    沈念sama閱讀 212,383評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件怠苔,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡仪糖,警方通過查閱死者的電腦和手機(jī)柑司,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,522評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來锅劝,“玉大人攒驰,你說我怎么就攤上這事」示簦” “怎么了玻粪?”我有些...
    開封第一講書人閱讀 157,852評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我劲室,道長伦仍,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,621評(píng)論 1 284
  • 正文 為了忘掉前任很洋,我火速辦了婚禮呢铆,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘蹲缠。我一直安慰自己棺克,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,741評(píng)論 6 386
  • 文/花漫 我一把揭開白布线定。 她就那樣靜靜地躺著娜谊,像睡著了一般。 火紅的嫁衣襯著肌膚如雪斤讥。 梳的紋絲不亂的頭發(fā)上纱皆,一...
    開封第一講書人閱讀 49,929評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音芭商,去河邊找鬼派草。 笑死,一個(gè)胖子當(dāng)著我的面吹牛铛楣,可吹牛的內(nèi)容都是我干的近迁。 我是一名探鬼主播,決...
    沈念sama閱讀 39,076評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼簸州,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼鉴竭!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起岸浑,我...
    開封第一講書人閱讀 37,803評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤搏存,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后矢洲,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體璧眠,經(jīng)...
    沈念sama閱讀 44,265評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,582評(píng)論 2 327
  • 正文 我和宋清朗相戀三年读虏,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了责静。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,716評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡掘譬,死狀恐怖泰演,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情葱轩,我是刑警寧澤睦焕,帶...
    沈念sama閱讀 34,395評(píng)論 4 333
  • 正文 年R本政府宣布藐握,位于F島的核電站,受9級(jí)特大地震影響垃喊,放射性物質(zhì)發(fā)生泄漏猾普。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,039評(píng)論 3 316
  • 文/蒙蒙 一本谜、第九天 我趴在偏房一處隱蔽的房頂上張望初家。 院中可真熱鬧,春花似錦乌助、人聲如沸溜在。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,798評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽掖肋。三九已至,卻和暖如春赏参,著一層夾襖步出監(jiān)牢的瞬間志笼,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,027評(píng)論 1 266
  • 我被黑心中介騙來泰國打工把篓, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留纫溃,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,488評(píng)論 2 361
  • 正文 我出身青樓韧掩,卻偏偏與公主長得像紊浩,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子揍很,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,612評(píng)論 2 350

推薦閱讀更多精彩內(nèi)容

  • 1.問題背景 為了支持靈活的配置:配置項(xiàng)可隨時(shí)按需增減郎楼,我們應(yīng)用的配置文件采用了JSON格式的配置文件。其整體是一...
    西瓜雪梨桔子汁閱讀 5,101評(píng)論 0 0
  • 概要 64學(xué)時(shí) 3.5學(xué)分 章節(jié)安排 電子商務(wù)網(wǎng)站概況 HTML5+CSS3 JavaScript Node 電子...
    阿啊阿吖丁閱讀 9,146評(píng)論 0 3
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對(duì)...
    cosWriter閱讀 11,093評(píng)論 1 32
  • 第5章 引用類型(返回首頁) 本章內(nèi)容 使用對(duì)象 創(chuàng)建并操作數(shù)組 理解基本的JavaScript類型 使用基本類型...
    大學(xué)一百閱讀 3,219評(píng)論 0 4
  • 第一部分 HTML&CSS整理答案 1. 什么是HTML5窒悔? 答:HTML5是最新的HTML標(biāo)準(zhǔn)。 注意:講述HT...
    kismetajun閱讀 27,450評(píng)論 1 45