Python中優(yōu)雅地處理JSON5文件的方法詳解
JSON5 概述
JSON5 是 JSON 的一個超集,通過引入部分 ECMAScript 5.1 的特性來擴展 JSON 的語法,以減少 JSON 格式的某些限制。同時,保持兼容現(xiàn)有的 JSON 格式。從官網(wǎng)作者介紹來看,JSON5 注重的是更人性化的編寫和維護,一般用于軟件的配置文件場景。
JSON5 拓展了 JSON 的能力,支持以下特性:
注釋
尾隨逗號
單引號
字符串字面量
數(shù)字 (包括 Infinity, NaN, hexadecimal)
對象/數(shù)組字面量
多行字符串
格式官方文檔 spec.json5.org/
遇到的問題
最近團隊開始對接華為鴻蒙系統(tǒng),在鴻蒙工程中,配置文件都是 json5 文件格式。例如存儲 APP 信息的 app.json5
{ "app": { // 包名 "bundleName": "com.xxx.sample", // 廠家信息 "vendor": "sample", // 版本號 "versionCode": 1000000, "versionName": "1.0.0", "icon": "$media:app_launcher", "label": "$string:app_name", "generateBuildHash": true } }
JSON5 文件中會存儲數(shù)據(jù)信息和注釋信息,有助于在閱讀的時候了解數(shù)據(jù)結構。
需求:腳本修改 JSON5 文件時保留注釋信息
需求:在 CD 構建時,根據(jù)傳入的版本號修改 app.json5 中的版本號信息,然后將修改的文件提交到對應的版本分支上。
處理方式:因為是 python 的腳本,所以就找了 python 中可以操作 json5 的庫,首先就是最常見的 json5 庫,它提供與標準 json 庫類似的 API,可以讀寫 json5 文件,例如下面的例子
import json5 data = json5.load(open('app.json5','r')) print(json5.dumps(data, indent=4)) // 輸出結果 { "app": { "bundleName": "com.xxx.sample", "vendor": "sample", "versionCode": 1000000, "versionName": "1.0.0", "icon": "$media:app_launcher", "label": "$string:app_name", "generateBuildHash": true } }
可以看到雖然 API 可以正確的解析和輸出文件數(shù)據(jù),但是注釋信息卻沒有了。使用官方推薦的 nodejs 版本 json5 庫也是一樣的結果。也有人開 issue 提需求是否可以提供 API 可以保留注釋信息,但是作者最終還是暫時婉拒了這個需求
后續(xù)雖然有人提交了支持這個 feature 的 PR,但是也是遲遲沒有合入。所以目前來說較為官方的庫都是沒有支持讀寫 json5 文件時保留注釋信息的。
雖然在搜索解決方案的時候,也有人提出說直接使用一個字段(例如"__comment__")存儲注釋信息,但是個人認為這是非常不優(yōu)雅的:明明 JSON5 推出就是支持注釋的,最終又要回退到 JSON。
解決方式
雖然官方庫沒有支持讀寫時保留注釋信息,但是還是有部分擴展庫是支持的。這些擴展庫也在 JSON5 的 Github 的 Wiki 中 In-the-Wild 部分列舉了出來。
json-five
其中 json-five 這個庫支持讀寫 JSON5 文件時保留注釋信息。
下面是官方提供的一個 demo
from json5.loader import loads, ModelLoader from json5.dumper import dumps, ModelDumper from json5.model import BlockComment json_string = """{"foo": "bar"}"""model = loads(json_string, loader=ModelLoader())print(model.value.key_value_pairs[0].value.wsc_before) # [' '] model.value.key_value_pairs[0].key.wsc_before.append(BlockComment("/* comment */")) dumps(model, dumper=ModelDumper()) # '{/* comment */"foo": "bar"}'
可以看出,雖然 json-five 支持了保留注釋信息,但是在數(shù)據(jù)的操作上非常麻煩,基本不能像使用 json 庫時將數(shù)據(jù)當做 dict 進行操作,這樣很不優(yōu)雅。
擴展 json-five
于是對現(xiàn)有數(shù)據(jù)結構進行了擴展,支持[]操作符進行獲取或者賦值,簡化 json5 操作流程。
# -*- coding: UTF-8 -*- ''' 支持保留注釋和格式的JSON5處理工具 ''' # pip3 install json-five import json5 from json5.dumper import modelize from json5.model import JSONArray, JSONObject, String, JSONText, Value, KeyValuePair, walk # 重寫JSONObject的__getitem__方法,支持通過字符串獲取值,如果不存在則返回None def _find(self, key): if isinstance(key, str) and isinstance(self, JSONObject): for item in self.key_value_pairs: if isinstance(item.key, String): if item.key.characters == key: return item.value elif isinstance(key, int) and isinstance(self, JSONArray): return self.values[key] elif isinstance(self, JSONText): return self.value[key] return None # 重寫JSONObject的__setitem__方法,支持通過字符串設置值,如果不存在則拋出異常 def _jsonobj_set(self: JSONObject, key: str, value: Value): new_item = KeyValuePair(modelize(key), value) for index in range(len(self.key_value_pairs)): item = self.key_value_pairs[index] if isinstance(item.key, String): if item.key.characters == key: old_value = self.values[index] new_item.value.wsc_after = old_value.wsc_after new_item.value.wsc_before = old_value.wsc_before new_item.value._tok = old_value._tok new_item.value._end_tok = old_value._end_tok self.values[index] = new_item.value return raise KeyError(key) # self.keys.append(new_item.key) # self.values.append(new_item.value) # 重寫JSONArray的__setitem__方法,支持通過整數(shù)設置值,如果不存在則拋出異常,如果存在則覆蓋原值 def _jsonarray_set(self: JSONArray, index: int, value: Value): self.values[index] = value # 重寫JSONObject的str_keys方法,支持返回所有字符串類型的keys,如果不存在則返回[] def _jsonobj_str_keys(self: JSONObject): return [item.characters for item in self.keys if isinstance(item, String)] JSONObject.__getitem__ = _find JSONObject.__setitem__ = _jsonobj_set JSONObject.str_keys = _jsonobj_str_keys JSONArray.__getitem__ = _find JSONArray.__setitem__ = _jsonarray_set JSONText.__getitem__ = _find # 加載JSON5文件,保留注釋和格式,返回一個Model對象 def loadjson5_with_comment(path: str): return json5.load(open(path, 'r'), loader=json5.loader.ModelLoader()) # 保存JSON5文件,保留注釋和格式 def savejson5_with_comment(data, path: str): return json5.dump(data, open(path, 'w'), dumper=json5.dumper.ModelDumper()) # 尋找所有JSONObject中key為keyword的對象,返回一個列表,如果不存在則返回[] def find_jsonobjects(model, keyword: str) -> list[JSONObject]: items = [] for item in walk(model): if isinstance(item, JSONObject): for key in item.keys: if isinstance(key, String) and key.characters == keyword: items.append(item) return items
最終實現(xiàn)以下效果,最大限度地保留的文件格式和注釋信息,優(yōu)雅地滿足了需求
file_path = 'app.json5' model = loadjson5_with_comment(file_path) model['app']['versionName'] = modelize('1.1.1') savejson5_with_comment(model, file_path) # 修改后文件內(nèi)容 { "app": { // 包名 "bundleName": "com.xxx.sample", // 廠家信息 "vendor": "sample", // 版本號 "versionCode": 1000000, "versionName": '1.1.1', "icon": "$media:app_launcher", "label": "$string:app_name", "generateBuildHash": true } }
其他庫
從 In-the-Wild 中可以看到有很多庫支持 json5,但是測試前面的幾個 python 和 js 的庫,目前只有 json-five 這個支持保留注釋信息(也可能是我使用姿勢問題?)
總結
JSON5 作為 JSON 的擴展,提供了更人性化的語法,非常適合靜態(tài)配置文件場景,可以目前官方的庫 API 讀寫文件時不支持保留注釋信息(往往可能是配置文件中關鍵信息),在一些自動化場景稍顯不便。
雖然目前可以通過三方庫+擴展的方式達到一個基本可用的狀態(tài),還是希望官方能對此能力進行支持,讓 JSON5 的處理更優(yōu)雅~
以上就是Python中優(yōu)雅地處理JSON5文件的方法詳解的詳細內(nèi)容,更多關于Python處理JSON5文件的資料請關注腳本之家其它相關文章!
相關文章
Python爬蟲urllib和requests的區(qū)別詳解
這篇文章主要介紹了Python爬蟲urllib和requests的區(qū)別詳解,本篇文章通過簡要的案例,講解了該項技術的了解與使用,以下就是詳細內(nèi)容,需要的朋友可以參考下2021-09-09Python中使用dwebsocket實現(xiàn)后端數(shù)據(jù)實時刷新
dwebsocket是Python中一款用于實現(xiàn)WebSocket協(xié)議的庫,可用于后端數(shù)據(jù)實時刷新。在Django中結合使用dwebsocket和Channels,可以實現(xiàn)前后端的實時通信,支持雙向數(shù)據(jù)傳輸和消息推送,適用于實時聊天、數(shù)據(jù)監(jiān)控、在線游戲等場景2023-04-04django第一個項目127.0.0.1:8000不能訪問的解決方案詳析
django項目服務啟動后無法通過127.0.0.1訪問,下面這篇文章主要給大家介紹了關于django第一個項目127.0.0.1:8000不能訪問的解決方案,需要的朋友可以參考下2022-10-10Python中用pyinstaller打包時的圖標問題及解決方法
這篇文章主要介紹了python中用pyinstaller打包時的圖標問題及解決方法,本文從兩方面給大家分析原因所在,通過截圖實例代碼給大家介紹的非常詳細,需要的朋友可以參考下2020-02-02Python http接口自動化測試框架實現(xiàn)方法示例
這篇文章主要介紹了Python http接口自動化測試框架實現(xiàn)方法,結合實例形式分析了Python針對http接口測試的相關實現(xiàn)與使用操作技巧,需要的朋友可以參考下2018-12-12