你是否曾经在开发 Web 应用、处理 API 接口或者保存配置文件时,需要一种既轻量又易于人类阅读的数据格式?相信大多数开发者都会回答:是的,而且是 JSON。它如今已经成为了数据交换领域的通用语言。在这篇文章中,我们将深入探讨如何使用 Python 来高效地读取和写入 JSON 文件,不仅涵盖基础知识,还会结合 2026年的开发趋势,聊聊在 AI 辅助编程和云原生环境下,我们如何写出更具韧性的代码。
JSON 基础与 Python 的映射
在开始编写代码之前,让我们先简单回顾一下 JSON 的基本概念。JSON 的全称是 JavaScript 对象表示法(JavaScript Object Notation)。这是一种基于文本的轻量级数据交换格式,设计初衷是让人类容易阅读和编写,同时也便于机器解析和生成。
尽管它的名字来源于 JavaScript,但 JSON 已经独立于语言,成为了 Python 开发者最亲密的伙伴之一。Python 通过内置的 json 模块为我们提供了强大的支持,让我们无需安装任何第三方库即可处理 JSON 数据。
JSON 的数据结构看起来非常眼熟,对吧?
- 数据在键值对中,包裹在花括号
{}中,这看起来和 Python 的 字典 非常相似。 - 有序的数据列表包裹在方括号
[]中,这对应着 Python 的 列表。
为了更好地理解这两种语言之间的映射关系,我们可以参考下表,这将帮助我们在转换数据时避免类型错误:
JSON 对象
—
object
array
string
number
true / false
null
准备工作很简单,我们只需要在脚本开头导入标准库:
import json
好了,现在让我们进入正题。我们将 JSON 数据的处理分为两个核心动作:读取(反序列化) 和 写入(序列化)。
—
第一部分:从文件读取 JSON 数据(生产级实践)
在 Python 中读取 JSON 意味着从文件或字符串中检索 JSON 数据,并将其转换为 Python 能够理解的对象(通常是字典或列表)。这个过程被称为 反序列化(Deserialization)。
在 2026 年,随着配置文件即代码和微服务的普及,处理 JSON 文件变得更加频繁。你可能会遇到两种主要的情况:从一个实际的 .json 文件读取,或者从一个网络请求返回的字符串读取。让我们逐一来看,但这次我们不仅要看“怎么写”,还要看“怎么写才安全”。
1. 使用 json.load():安全的文件读取
这是最常用的场景。当你有一个存储数据的 JSON 文件时,json.load() 函数可以将文件内容直接加载为 Python 对象。它接受一个文件对象作为参数。
让我们看一个实际的例子。 假设我们有一个名为 data.json 的文件,内容如下:
{
"name": "Francis",
"age": 25,
"city": "New York",
"skills": ["Python", "Data Analysis"]
}
我们可以使用以下代码将其读取为 Python 字典。注意,这里我们采用了更严谨的编码声明和异常处理,这在处理跨国项目或混合编码数据时至关重要:
import json
file_path = "data.json"
try:
# 使用 ‘with‘ 语句打开文件是最佳实践,它可以确保文件在操作完成后自动关闭
# 显式指定 encoding=‘utf-8‘ 可以避免 Windows 和 macOS 之间的潜在兼容性问题
with open(file_path, "r", encoding="utf-8") as f:
# 将 JSON 文件内容解析为 Python 字典
data = json.load(f)
# 打印结果
print("读取到的数据:", data)
print("数据类型:", type(data))
# 你现在可以像操作字典一样访问数据
print(f"用户的名字是: {data[‘name‘]}")
except FileNotFoundError:
print(f"错误:找不到文件 {file_path},请检查路径。")
except json.JSONDecodeError:
print(f"错误:文件 {file_path} 的内容损坏或格式错误。")
except Exception as e:
print(f"发生了未预期的错误: {e}")
输出结果:
读取到的数据: {‘name‘: ‘Francis‘, ‘age‘: 25, ‘city‘: ‘New York‘, ‘skills‘: [‘Python‘, ‘Data Analysis‘]}
数据类型:
用户的名字是: Francis
代码解析:
-
encoding="utf-8": 在 2026 年,全球化协作是常态。显式声明 UTF-8 是防止中文、Emoji 等字符在非 Linux 环境下乱码的第一道防线。 - INLINECODEe9a76a6a: 核心步骤。它读取文件对象 INLINECODE1d9c5818,解析 JSON 格式,并将其转换为 Python 字典。
- 异常处理: 在生产环境中,文件可能不存在,或者被外部进程写入了一半(脏数据)。使用 INLINECODE5ea4f3d7 捕获 INLINECODE06ce0dc2 是避免服务崩溃的标配。
2. 使用 json.loads():解析 API 响应字符串
有时候,数据并不在文件里,而是存储在一个字符串变量中。这在处理 API 响应或内存中的数据时非常常见。这时我们需要使用 json.loads()(注意 ‘s‘ 代表 string)。
示例代码:
import json
# 模拟从 API 接收到的 JSON 格式字符串
api_response = ‘{"id": 101, "title": "深入学习 Python", "published": true, "tags": ["coding", "ai"]}‘
try:
# 使用 json.loads 将字符串转换为 Python 字典
parsed_data = json.loads(api_response)
print("解析后的 ID:", parsed_data["id"])
print("文章是否已发布:", parsed_data["published"])
except json.JSONDecodeError as e:
print(f"API 数据解析失败: {e}")
关键区别:
json.load(): 作用于 文件对象(File Object)。json.loads(): 作用于 字符串对象(String Object)。
如果你不小心把文件对象传给了 loads,Python 会抛出 AttributeError,这是新手常犯的错误,也是我们使用 AI 辅助编程时偶尔会遇到的“幻觉”问题,所以理解原理依然重要。
—
第二部分:将 JSON 数据写入文件(序列化艺术)
学会了读取,接下来我们学习如何保存数据。在 Python 中将数据写入 JSON 文件涉及将字典等 Python 对象转换为 JSON 格式的字符串,并将其保存到文件中。这个过程被称为 序列化(Serialization)。
同样,我们有两种主要的方法:INLINECODEe170347f 和 INLINECODE480355e9。但在现代开发中,我们更强调数据的可读性和原子性。
1. 使用 json.dumps():格式化与预处理
dumps(dump string) 方法不会直接写入文件,而是先将 Python 对象转换成 JSON 格式的字符串。这在你需要通过网络发送数据,或者对数据进行预处理(比如签名验证)时非常有用。
它有几个非常有用的参数,可以让你控制输出格式,这也是区分“新手代码”和“专业代码”的细节:
indent: 缩进空格数,让输出更美观(通常设为 4)。sort_keys: 设为 True 可以让键名按字母排序。这在版本控制和代码对比中非常有用,因为它消除了因插入顺序不同导致的无意义的 diff。- INLINECODE06be1edb: 设为 False 可以输出中文字符,而不是 Unicode 编码(如 INLINECODE4b51c775),这对于调试至关重要。
示例:创建一个人类可读的 JSON 字符串
import json
# Python 字典数据
data_dict = {
"name": "sathiyajith",
"rollno": 56,
"cgpa": 8.6,
"subjects": ["Math", "CS", "Physics"]
}
# 将字典转换为 JSON 字符串,使用缩进美化并排序键名
json_str = json.dumps(data_dict, indent=4, sort_keys=True, ensure_ascii=False)
print("转换后的 JSON 字符串:")
print(json_str)
# 现在我们可以将这个字符串写入文件
with open("student_data.json", "w", encoding="utf-8") as f:
f.write(json_str)
print("
数据已成功写入 student_data.json")
输出结果:
{
"cgpa": 8.6,
"name": "sathiyajith",
"rollno": 56,
"subjects": [
"Math",
"CS",
"Physics"
]
}
注意观察: sort_keys=True 确保了无论你的程序如何构建这个字典,生成的文件结构总是稳定的。这在 Git 提交配置文件时能省去很多麻烦。
2. 使用 json.dump():直接持久化与原子写入
如果你不需要预处理字符串,只是想把数据保存到文件,那么 json.dump() 是更直接的方法。
进阶技巧:原子写入
在我们最近的一个云原生项目中,我们发现如果在高并发下直接写入配置文件,可能会出现写入一半程序崩溃导致文件损坏的情况。为了解决这个问题,我们推荐使用“先写临时文件,再原子替换”的策略。
import os
import json
import tempfile
config = {
"database": {
"host": "localhost",
"port": 3306,
"username": "admin"
},
"debug_mode": False,
"max_connections": 10
}
# 目标文件路径
config_file = "config.json"
# 创建一个临时文件
# 这使得写入操作在崩溃时更安全
try:
with tempfile.NamedTemporaryFile("w", encoding="utf-8", dir=".", delete=False) as tmp_f:
# 写入临时文件
json.dump(config, tmp_f, indent=4, ensure_ascii=False)
temp_name = tmp_f.name
# 原子性替换:只有在写入完全成功后,才替换目标文件
os.replace(temp_name, config_file)
print(f"配置文件已安全写入 {config_file}")
except Exception as e:
print(f"写入失败: {e}")
# 如果出错,记得清理可能残留的临时文件
if os.path.exists(temp_name):
os.remove(temp_name)
在这个例子中,即使程序在写入过程中被强制终止,原有的 INLINECODEaf2a0d9a 也不会变成一个空文件或损坏的文件,因为 INLINECODE061d1a78 是一个原子操作(在大多数现代操作系统上)。
—
第三部分:处理复杂对象与类型陷阱(2026 进阶指南)
掌握了基本操作后,让我们看看在实际开发中如何避免陷阱,特别是处理非标准 JSON 数据类型。
1. 处理非序列化对象
你可能会遇到这样的情况:你想存储一个 datetime 对象或者一个自定义的类实例,但 JSON 标准并不直接支持这些类型。
import json
from datetime import datetime
now = datetime.now()
# json.dumps(now) # 这会报错:Object of type datetime is not JSON serializable
解决方案:使用 INLINECODE47d1ccfe 参数和 INLINECODE111f9f7b 类。
为了保持代码的整洁和可复用,我们通常定义一个自定义的编码器,或者使用 lambda 函数。这里展示一个更符合现代工程规范的方案。
import json
from datetime import datetime
from decimal import Decimal
class AdvancedJSONEncoder(json.JSONEncoder):
"""
自定义 JSON 编码器,用于处理特殊类型。
在 2026 年,我们可能还需要处理 UUID、Decimal 等更多类型。
"""
def default(self, obj):
if isinstance(obj, datetime):
return obj.isoformat()
if isinstance(obj, Decimal):
return float(obj)
if isinstance(obj, set):
return list(obj)
# 如果是无法处理的类型,调用父类方法抛出错误
return super().default(obj)
# 使用自定义编码器
data = {
"event": "System Login",
"timestamp": datetime.now(),
"amount": Decimal("99.99"),
"tags": {"admin", "user"}
}
# 在 dumps 中指定 cls 参数
json_str = json.dumps(data, cls=AdvancedJSONEncoder, indent=4)
print(json_str)
这样做的好处是将转换逻辑封装在类中,你的主业务逻辑会非常干净。这在 AI 辅助编程时也能减少模型对上下文的理解负担。
2. 2026年的性能优化:ORJSON 的崛起
虽然 Python 内置的 json 模块对于大多数应用已经足够快,但在 2026 年,随着数据处理量的激增和 AI 应用对延迟的敏感,我们可能需要更快的速度。
前沿视角: 当我们在微服务架构中处理大量 JSON 日志或实时数据流时,内置库可能成为瓶颈。这时我们通常会选择 orjson 这样的第三方库。它是用 Rust 编写的,速度通常是标准库的 2-3 倍,且序列化后的体积更小。
为什么我们在这里提到它? 因为了解技术选型是进阶工程师的标志。虽然本文重点在标准库,但在生产环境中,如果你发现 json.dump() 占用了大量 CPU 时间,不妨尝试:
# pip install orjson
import orjson
# orjson 的 API 略有不同,它直接返回 bytes,且默认不处理 ensure_ascii
data = {"message": "你好,世界", "speed": "fast"}
# 序列化
json_bytes = orjson.dumps(data)
print(json_bytes.decode("utf-8"))
3. 容灾与调试:LLM 时代的 JSON 处理
在与 LLM(大语言模型)交互时,我们经常遇到模型返回的文本夹杂在 Markdown 代码块中,或者包含了多余的解释性文字。这就需要我们使用更健壮的解析方式,而不是简单的 json.loads。
场景:从 AI 输出中提取 JSON
import json
import re
raw_ai_output = """
这是我的分析结果:
json
{
"reasoning": "用户可能想查询天气",
"action": "search_weather"
}
希望这对您有帮助。
"""
# 简单的清洗策略:提取第一个花括号包裹的块
def extract_json(text):
# 尝试匹配 markdown 代码块
match = re.search(r‘
json
(.*?)
“INLINECODEe1ea25a6`INLINECODEc1c38d61datetimeINLINECODEcd76da1aloadsINLINECODEacc0b3fadumpsINLINECODE04213d2aloadINLINECODE58f576ffdump`): 用于处理 文件对象。
掌握 JSON 处理是每一个 Python 开发者的必备技能。无论你是要保存程序的配置,还是要对接复杂的 REST API,亦或是构建下一个 AI 应用,这些知识都将是你工具箱里最锋利的一把刀。现在,尝试在你的下一个项目中应用原子写入或自定义编码器,你会发现代码的健壮性提升了一个档次。