在前后端接口交互、配置文件处理、日志导出等日常开发场景中,JSON 是使用最广泛的数据交换格式之一。Python 内置的 json 模块提供了高效的序列化与反序列化能力,其中 json.loads() 负责把 JSON 字符串还原为 Python 对象,json.dump() 负责把 Python 对象序列化并写入 JSON 文件。理解并灵活使用这两个方法,是处理 API 响应、数据配置和本地存储的基础技能。
一、json.loads():将 JSON 字符串解析为 Python 对象
json.loads() 接收一个合法的 JSON 字符串,并返回对应的 Python 原生数据类型,如 dict、list、str、int、bool 或 None。由于 JSON 对象本质上等同于 Python 字典,所以解析后可以像操作普通字典一样访问字段。
下面的代码演示了最基本的解析过程:
- import json
- json_str = '{"name": "Alice", "age": 30, "city": "New York"}'
- data = json.loads(json_str)
- print(data)
- print(type(data))
- print(data["name"])
复制代码
运行结果会依次输出字典内容、类型 dict 以及字段值 Alice。需要注意的是,JSON 字符串中必须使用双引号,不能使用单引号;如果输入字符串不符合 JSON 语法,会抛出 json.JSONDecodeError。
json.loads() 也支持解析 JSON 数组字符串,解析后得到 Python 列表:
- import json
- json_arr = '[1, 2, 3, "hello"]'
- py_list = json.loads(json_arr)
- print(py_list)
复制代码
输出为 [1, 2, 3, 'hello']。除了最常用的默认解析,json.loads() 还提供了一些高阶参数,例如 object_hook 可以在字典解码后执行自定义逻辑,parse_float 可以指定浮点数解析类型(如 Decimal)。这些参数在普通业务中较少使用,但遇到特殊数据格式时能派上用场。
二、json.dump():将 Python 对象写入 JSON 文件
json.dump() 与 json.dumps() 不同:json.dumps() 返回 JSON 字符串,而 json.dump() 直接将序列化结果写入文件对象。其典型用法是配合 open() 打开一个文件,再通过 fp 参数传入文件句柄。
基础写法如下:
- import json
- data = {
- "title": "Hello World",
- "content": "This is a test article.",
- "author": "John"
- }
- with open("output.json", "w", encoding="utf-8") as f:
- json.dump(data, f, ensure_ascii=False, indent=4)
复制代码
执行后 output.json 文件中的内容会是带有 4 个空格缩进且未进行 Unicode 转义的 JSON 对象。这里有两个关键参数需要掌握:
ensure_ascii=False 表示将中文等非 ASCII 字符原样输出,而不是默认转义成 \uxxxx 形式;indent=4 则控制缩进级别,让文件内容具备可读性。除此之外,json.dump() 还支持 sort_keys(按字典键排序输出)和 separators(自定义元素分隔符,例如 (',', ': '))等参数。
如果文件中需要出现中文,尤其是当作配置文件或导出数据时,务必显式指定 ensure_ascii=False,否则生成的内容会变得难以直接阅读。
三、组合使用 json.load() 和 json.dump():读取修改后写回
json.dump() 的典型工作流不只是简单写入,而是先读取已有 JSON 文件,修改字段后再写回。读取文件时通常使用 json.load(),它从文件对象中解析 JSON;写入时使用 json.dump()。二者配合,可以完成一次完整的“读取-修改-落盘”过程:
- import json
- # 读取原始 JSON 文件
- with open("input.json", "r", encoding="utf-8") as f:
- data = json.load(f)
- # 修改数据
- data["status"] = "published"
- # 写回新文件
- with open("output.json", "w", encoding="utf-8") as f:
- json.dump(data, f, ensure_ascii=False, indent=4)
复制代码
在实际项目中,这种模式常被用于配置更新、状态切换和批处理任务。需要注意的是,json.load() 和 json.loads() 的参数不同:json.load() 接收文件对象,json.loads() 接收字符串对象,二者不能混用。
四、实战场景一:解析 API 返回的 JSON 响应
调用远程接口时,requests 库返回的 response.text 通常是 JSON 字符串,直接使用 json.loads() 可以快速转换为可遍历的 Python 对象:
- import requests
- import json
- response = requests.get("https://api.example.com/data")
- data = json.loads(response.text)
- print(data["key"])
复制代码
这段代码的前提是接口确实返回合法 JSON。如果返回空内容或错误页,json.loads() 会抛出异常,因此生产代码里建议增加异常捕获或状态码判断,不过这不是本文重点。
五、实战场景二:读取 JSON 配置文件
很多项目会用 JSON 文件保存数据库连接信息、日志级别等配置。使用 json.load() 读取后,可以直接通过嵌套字典的 key 访问配置项:
- import json
- with open("config.json", "r", encoding="utf-8") as f:
- config = json.load(f)
- print(config["database"]["host"])
复制代码
六、实战场景三:封装通用的数据导出函数
如果多个接口或脚本都需要把 Python 对象导出为 JSON 文件,可以封装一个小函数,统一缩进和编码行为:
- import json
- def export_to_json(data, filename):
- with open(filename, "w", encoding="utf-8") as f:
- json.dump(data, f, indent=2, ensure_ascii=False)
复制代码
之后在任何地方只需调用 export_to_json(data, "result.json") 即可生成格式化文件,避免重复书写打开文件与 dump 的逻辑。
七、实战场景四:使用自定义编码器处理 datetime 对象
JSON 无法直接序列化 datetime 类型,默认调用 json.dump() 时会报 TypeError。解决办法是继承 json.JSONEncoder 并重写 default 方法,把 datetime 对象转换为 ISO 格式字符串:
- import json
- from datetime import datetime
- class DateTimeEncoder(json.JSONEncoder):
- def default(self, obj):
- if isinstance(obj, datetime):
- return obj.isoformat()
- return super().default(obj)
- data = {"timestamp": datetime.now()}
- with open("time.json", "w") as f:
- json.dump(data, f, cls=DateTimeEncoder)
复制代码
这样 time.json 中的 timestamp 就会以类似 2025-01-01T12:30:45 的字符串格式落盘。需要时还可以在读取后使用 datetime.fromisoformat() 还原为时间对象。
总结:json.loads() 负责把 JSON 字符串转换为 Python 对象,突出的是一个“解析”;json.dump() 负责把 Python 对象写入文件,突出的是一个“序列化与持久化”。掌握这两个方法,再配合 json.load() 读取文件,就能覆盖大多数 JSON 数据处理需求。后续在爬虫解析、后端接口开发或数据分析过程中,你会发现这两个方法几乎无处不在。 |