Loading... # Python 读写 JSON 笔记 Python 非常适合写自动化脚本,因此这里简单记录一下平时写自动化的时候经常用到的序列化与反序列化 JSON 的功能。 ## 读取 JSON 文件 如果是字符串变量形式的直接调用 `json.loads(变量名)` 就能够将字符串转化为 `dict` 类型。如果是文件类型的那么通常可以用 `open(路径, "r")` 打开文件,并用 `json.load(打开的文件)` 对文件内容进行读取并转换为 `dict` 类型。 ```Python import json def main(): # read json dict from string jsonContent = "{\"name\":\"henry petter\", \"id\":123456}" tmp = json.loads(jsonContent) print(f"type of data: {type(tmp)}") print(f'{tmp["name"]}') # read json content from file path = "./test.json" with open(path, "r") as f: data = json.load(f) data = str(data) print(f"content of data: {data}") print(f"type of data: {type(data)}") if __name__ == "__main__": main() ``` 通常直接这样就可以读取并转换为 `dict` 类型。 ### 编码问题 值得注意的是遇到非 ASCII 内容的时候直接通过 `open(路径, "r")` 和 `json.load(文件)` 读取可能会出现乱码,例如说假设我们的 JSON 文件内容包含了非 ASCII 编码的字符: ```JSON { "name": "Diu Lei", "hobbies": ["调理农务", "请你好好放低", "running"], "chineseName": "雷丢", "gender": "female" } ``` 那么直接用上一节的 Python 脚本来读取内容并打印的话,会出现这样的乱码输出: ```sh type of data: <class 'dict'> henry petter content of data: {'name': 'Diu Lei', 'hobbies': ['璋冪悊鍐滃姟', '璇蜂綘濂藉ソ鏀句綆', 'running'], 'chineseName': '闆蜂涪', 'gender': 'female'} type of data: <class 'str'> ``` 这是因为读取文件的时候试图以 ASCII 编码打开文件并读取,而 `json.loads` 又以 UTF-8 编码进行解析(`json.loads` 要求输入的编码是 UTF-8, UTF-16 或 UTF-32),最终打印出乱码[^1]。这里的乱码可以通过乱码工具进行恢复: ![20240412193803](https://images-1300215216.cos.ap-guangzhou.myqcloud.com/Blog/20240412193803.png) 我们可以尝试对读取到的 JSON 内容进行重新编码: ```python # 省略部分代码 path = "./test2.json" with open(path, "r") as f: data = json.load(f) # 修改编码 data = str(data).encode("gbk").decode("utf-8") print(f"content of data: {data}") ``` 重新运行程序,可以看到输出正常了: ```shell content of data: {'name': 'Diu Lei', 'hobbies': ['调理农务', '请你好好放低', 'running'], 'chineseName': '雷丢', 'gender': 'female'} ``` 但是只能验证我们的猜想,实际上我们不一定总能确定 JSON 文件中只包含 GBK 编码的中文字符,有可能也存在其他编码的文本,因此更加合适的读取方式应当为: ```python path = "./test2.json" with open(path, "r", encoding="utf8") as f: data = json.load(f) print(f"decode data with utf8:\n{data}") ``` 前提是 JSON 文件确实是以 `utf-8` 编码保存的。这其实是文件读写需要注意的事情,留意一下文件的编码即可。 ## 写入 JSON 文件 ### 序列化 通常调用 JSON 库的 `json.dump` 和 `json.dumps` 来序列化,前者用于将一个 `obj` 序列化成 JSON 格式流支持的 file pointer。 #### 序列化为 string 我们先看看如何序列化 `dict` 成 `string`。 ```python def demoW2() -> None: """将 dict 序列化为 string demo """ student = dict() student["name"] = "Henry Petter" student["age"] = "28" student["mantra"] = "丢丢丢" student["zh_name"] = "雷丢" res1 = json.dumps(student) print(f"default serialization res:\n{res1}") res = json.dumps(student, ensure_ascii=False) print(f"ensure_ascii=False serialization res:\n{res}") ``` 打印结果如下: ```bash default serialization res: {"name": "Henry Petter", "age": "28", "mantra": "\u4e22\u4e22\u4e22", "zh_name": "\u96f7\u4e22"} ensure_ascii=False serialization res: {"name": "Henry Petter", "age": "28", "mantra": "丢丢丢", "zh_name": "雷丢"} ``` 可以看到序列化过程中也会遇到编码问题,Python 默认会将非 ASCII 字符转换为 Unicode 编码地字符,因此如果希望最终转化之后的结果还是保持原有的编码,需要给参数 `ensure_ascii` 设置为 `False`。 在序列化为 `string` 之后即可正常写入到文件中或者通过网络传输。如果希望直接序列化并存入文件,那么建议使用 `json.dump`。 #### 序列化 dict 并写入文件 ```python def demoW3() -> None: """演示如何写入 JSON 到文件中 """ path = "testW1.json" jsonObj = {} jsonObj["name"] = "Henry Petter" jsonObj["age"] = "28" jsonObj["mantra"] = "丢丢丢" jsonObj["zh_name"] = "雷丢" with open(path, "w", encoding="utf-8") as f: json.dump(jsonObj, f, ensure_ascii=False) ``` 使用 `json.dump` 的时候要传入的东西和 `json.dumps` 差不多,值得注意的还是打开文件时候设置的编码 `encoding="utf-8"`,否则写入非 ASCII 编码的内容,且 `json.dump` 参数 `ensure_ascii=False` 的时候,最终文件内会出现乱码。 ## 参考 1. [官方文档json --- JSON 编码和解码器](https://docs.python.org/zh-cn/3/library/json.html) 2. [乱码恢复](http://www.mytju.com/classcode/tools/messycoderecover.asp) [^1]: `json.loads` 在 3.9 前可以传入 `encoding` 参数指定编码,但是 3.9 开始移除了这个参数,并默认传入的需要解析的文本都是 UTF-8 编码的。 最后修改:2024 年 04 月 20 日 © 允许规范转载 打赏 赞赏作者 支付宝微信 赞 随缘