← 文件操作 PyPDF2 →

📄 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 对象
← 文件操作 PyPDF2 →