📄 JSON 文件处理
JSON (JavaScript Object Notation) 是一种轻量级的数据交换格式,易于人阅读和编写,同时也易于机器解析和生成。Python 内置了 json 模块,可以方便地处理 JSON 数据。
JSON 特点:
- 📝 基于文本,人类可读
- 🔄 语言无关,支持多种编程语言
- ⚡ 解析速度快,效率高
- 📦 支持嵌套结构,表达复杂数据
一、JSON 基础
1.1 JSON 数据格式
{
"name": "张三",
"age": 25,
"city": "北京",
"skills": ["Python", "JavaScript", "SQL"],
"is_active": true,
"salary": null,
"address": {
"street": "中关村大街",
"number": "1 号"
}
}
1.2 Python 与 JSON 数据类型对应
| Python | JSON |
|---|---|
dict |
object |
list, tuple |
array |
str |
string |
int, float |
number |
True |
true |
False |
false |
None |
null |
1.3 导入 json 模块
# Python 内置 json 模块,无需安装
import json
二、JSON 基本操作
🔄 JSON 操作流程图
Python 对象
JSON 字符串
↓ json.dumps()
↑ json.loads()
序列化
反序列化
↓ json.dump()
↑ json.load()
JSON 文件
JSON 文件
2.1 序列化:Python → JSON
import json
# Python 字典
data = {
"name": "张三",
"age": 25,
"city": "北京",
"skills": ["Python", "JavaScript"],
"is_active": True
}
# 转换为 JSON 字符串
json_string = json.dumps(data)
print(json_string)
# 输出:{"name": "张三", "age": 25, "city": "北京", "skills": ["Python", "JavaScript"], "is_active": true}
# 带格式化的输出
json_string_pretty = json.dumps(data, ensure_ascii=False, indent=2)
print(json_string_pretty)
2.2 反序列化:JSON → Python
import json
# JSON 字符串
json_string = '''
{
"name": "张三",
"age": 25,
"city": "北京",
"skills": ["Python", "JavaScript"],
"is_active": true
}
'''
# 转换为 Python 字典
data = json.loads(json_string)
print(data) # {'name': '张三', 'age': 25, 'city': '北京', ...}
print(data['name']) # 张三
print(data['skills']) # ['Python', 'JavaScript']
2.3 读写 JSON 文件
import json
# 写入 JSON 文件
data = {
"name": "张三",
"age": 25,
"skills": ["Python", "JavaScript"]
}
with open('data.json', 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=2)
# 读取 JSON 文件
with open('data.json', 'r', encoding='utf-8') as f:
data = json.load(f)
print(data)
三、JSON 高级操作
3.1 常用参数说明
| 参数 | 说明 | 示例 |
|---|---|---|
ensure_ascii |
是否转义非 ASCII 字符 | ensure_ascii=False 显示中文 |
indent |
缩进空格数 | indent=2 美化输出 |
sort_keys |
是否按 key 排序 | sort_keys=True |
separators |
自定义分隔符 | separators=(',', ':') |
3.2 参数使用示例
import json
data = {
"name": "张三",
"age": 25,
"city": "北京"
}
# 默认(会转义中文)
print(json.dumps(data))
# {"name": "\u5f20\u4e09", "age": 25, "city": "\u5317\u4eac"}
# 显示中文 + 美化格式
print(json.dumps(data, ensure_ascii=False, indent=2))
# {
# "name": "张三",
# "age": 25,
# "city": "北京"
# }
# 按键排序 + 紧凑格式
print(json.dumps(data, ensure_ascii=False, sort_keys=True, separators=(',', ':')))
# {"age":25,"city":"北京","name":"张三"}
3.3 处理特殊数据类型
import json
from datetime import datetime
# 自定义类型
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
# 方法 1:自定义编码函数
def json_encoder(obj):
"""自定义 JSON 编码器"""
if isinstance(obj, datetime):
return obj.strftime('%Y-%m-%d %H:%M:%S')
if isinstance(obj, Person):
return {'name': obj.name, 'age': obj.age}
raise TypeError(f'Object of type {type(obj)} is not JSON serializable')
data = {
'time': datetime.now(),
'person': Person('李四', 30)
}
json_string = json.dumps(data, default=json_encoder, ensure_ascii=False)
print(json_string)
# 方法 2:继承 JSONEncoder
class CustomEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, datetime):
return obj.strftime('%Y-%m-%d %H:%M:%S')
if isinstance(obj, Person):
return {'name': obj.name, 'age': obj.age}
return super().default(obj)
json_string = json.dumps(data, cls=CustomEncoder, ensure_ascii=False)
3.4 处理大型 JSON 文件
import json
# 逐行读取大型 JSON 文件(JSON Lines 格式)
def read_large_json(filepath):
"""读取大型 JSON 文件(每行一个 JSON 对象)"""
with open(filepath, 'r', encoding='utf-8') as f:
for line in f:
yield json.loads(line)
# 使用示例
for record in read_large_json('large_data.jsonl'):
print(record)
# 流式写入
def write_json_lines(filepath, data_list):
"""写入 JSON Lines 文件"""
with open(filepath, 'w', encoding='utf-8') as f:
for item in data_list:
f.write(json.dumps(item, ensure_ascii=False) + '\n')
四、实战案例
案例 1:配置文件管理
import json
from pathlib import Path
class Config:
"""配置文件管理类"""
def __init__(self, config_file='config.json'):
self.config_file = config_file
self.config = self.load()
def load(self):
"""加载配置"""
if Path(self.config_file).exists():
with open(self.config_file, 'r', encoding='utf-8') as f:
return json.load(f)
return {}
def save(self):
"""保存配置"""
with open(self.config_file, 'w', encoding='utf-8') as f:
json.dump(self.config, f, ensure_ascii=False, indent=2)
def get(self, key, default=None):
"""获取配置值"""
return self.config.get(key, default)
def set(self, key, value):
"""设置配置值"""
self.config[key] = value
self.save()
# 使用示例
config = Config()
config.set('database', 'localhost')
config.set('port', 3306)
print(config.get('database'))
案例 2:API 响应处理
import json
import requests
def fetch_api_data(url):
"""获取 API 数据并处理"""
try:
response = requests.get(url)
response.raise_for_status()
# 解析 JSON 响应
data = response.json()
# 保存到文件
with open('api_response.json', 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=2)
return data
except requests.exceptions.RequestException as e:
print(f'请求错误:{e}')
except json.JSONDecodeError as e:
print(f'JSON 解析错误:{e}')
return None
# 使用示例
data = fetch_api_data('https://api.example.com/data')
案例 3:数据验证和转换
import json
from typing import Dict, List, Optional
class JSONValidator:
"""JSON 数据验证器"""
@staticmethod
def validate_schema(data: Dict, schema: Dict) -> bool:
"""验证数据是否符合 schema"""
for key, expected_type in schema.items():
if key not in data:
print(f"缺少必需字段:{key}")
return False
if not isinstance(data[key], expected_type):
print(f"字段 {key} 类型错误,期望 {expected_type}")
return False
return True
@staticmethod
def transform(data: Dict, mapping: Dict) -> Dict:
"""转换 JSON 数据结构"""
return {new_key: data.get(old_key) for old_key, new_key in mapping.items()}
# 使用示例
schema = {
'name': str,
'age': int,
'email': str,
'skills': list
}
data = {
'name': '张三',
'age': 25,
'email': 'zhangsan@example.com',
'skills': ['Python', 'Java']
}
if JSONValidator.validate_schema(data, schema):
print("数据验证通过")
# 转换数据结构
mapping = {'name': 'userName', 'email': 'emailAddress'}
transformed = JSONValidator.transform(data, mapping)
print(json.dumps(transformed, ensure_ascii=False, indent=2))
五、常见错误处理
5.1 JSON 解析错误
import json
# 常见错误 1:JSON 格式错误
invalid_json = "{'name': '张三'}" # 单引号,无效 JSON
try:
data = json.loads(invalid_json)
except json.JSONDecodeError as e:
print(f"JSON 解析错误:{e}")
# 常见错误 2:文件不存在
try:
with open('not_exist.json', 'r', encoding='utf-8') as f:
data = json.load(f)
except FileNotFoundError:
print("文件不存在")
# 常见错误 3:编码问题
try:
with open('data.json', 'r') as f: # 未指定 encoding
data = json.load(f)
except UnicodeDecodeError:
print("编码错误,请指定 encoding='utf-8'")
5.2 完善的错误处理示例
import json
from pathlib import Path
def safe_load_json(filepath, default=None):
"""安全加载 JSON 文件"""
try:
path = Path(filepath)
if not path.exists():
print(f"文件不存在:{filepath}")
return default
with open(path, 'r', encoding='utf-8') as f:
return json.load(f)
except json.JSONDecodeError as e:
print(f"JSON 格式错误:{e}")
return default
except Exception as e:
print(f"未知错误:{e}")
return default
def safe_save_json(filepath, data, **kwargs):
"""安全保存 JSON 文件"""
try:
# 确保目录存在
Path(filepath).parent.mkdir(parents=True, exist_ok=True)
with open(filepath, 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, **kwargs)
return True
except Exception as e:
print(f"保存失败:{e}")
return False
# 使用示例
data = safe_load_json('config.json', default={})
safe_save_json('output.json', data, indent=2)
六、最佳实践
✅ 推荐做法:
- 始终使用
encoding='utf-8'读写文件 - 使用
ensure_ascii=False显示中文 - 使用
indent=2美化输出便于阅读 - 使用
with语句自动管理文件资源 - 添加适当的错误处理
- 对于大文件使用 JSON Lines 格式
❌ 避免做法:
- 不要手动拼接 JSON 字符串(使用 json.dumps)
- 不要忽略编码问题
- 不要假设 JSON 数据一定有效(添加验证)
- 避免在 JSON 中使用复杂 Python 对象