Python 中读取和写入 JSON

'# Python 中读取和写入 JSON

一、背景与问题

JSON(JavaScript Object Notation)作为轻量级的数据交换格式,广泛应用于前后端数据通信、配置文件存储、日志记录等场景。在 Python 中,标准库提供了 json 模块,支持将 Python 对象与 JSON 格式数据进行转换。然而,实际开发中常遇到以下问题:

  • 如何处理嵌套结构的序列化/反序列化?
  • 如何在性能与可读性之间取得平衡?
  • 如何避免反序列化时的安全风险?
  • 当数据量极大时如何优化读写效率?

本文将深入解析 json 模块的底层原理,结合实际开发场景,探讨其适用边界和最佳实践。


二、基本原理

1. JSON 与 Python 数据类型映射

JSON 标准支持以下数据类型:

  • 对象(Object) ↔ Python 字典(dict)
  • 数组(Array) ↔ Python 列表(list)
  • 字符串(String) ↔ Python 字符串(str)
  • 数字(Number) ↔ Python 整数(int)、浮点数(float)
  • 布尔值(Boolean) ↔ Python 布尔值(bool)
  • 空值(null) ↔ Python None

注意:json 模块无法直接序列化以下类型:

  • datetime.datetime 等时间对象
  • 自定义类实例
  • set 集合
  • decimal.Decimal 等高精度数值类型

2. 序列化与反序列化流程

序列化(Serialization)过程将 Python 对象转换为 JSON 字符串:

Python 对象 → JSON 字符串(通过 json.dumps)

反序列化(Deserialization)过程将 JSON 字符串还原为 Python 对象:

JSON 字符串 → Python 对象(通过 json.loads)

底层实现采用递归遍历对象属性,通过 json.JSONEncoder 的 default() 方法处理非标准类型。


三、环境准备

确保 Python 3 环境已安装,基础依赖如下:

# 无需额外安装,json 模块是 Python 标准库

四、核心实现

1. 基础读写操作

代码示例 1:读写简单数据

import json

# 创建 Python 对象
data = {
    "name": "Alice",
    "age": 30,
    "is_student": False,
    "courses": ["Math", "Physics"]
}

# 序列化为 JSON 字符串
json_str = json.dumps(data, indent=2)
print("JSON 字符串:", json_str)

# 反序列化为 Python 对象
loaded_data = json.loads(json_str)
print("Python 对象:", loaded_data)

关键代码解释:

  • json.dumps() 使用 ensure_ascii=False 可保留中文字符
  • indent=2 参数控制输出格式化缩进
  • json.loads() 返回的字典保持原始数据类型

输出示例:

JSON 字符串: {
  "name": "Alice",
  "age": 30,
  "is_student": false,
  "courses": [
    "Math",
    "Physics"
  ]
}
Python 对象: {'name': 'Alice', 'age': 30, 'is_student': False, 'courses': ['Math', 'Physics']}

2. 处理复杂结构

代码示例 2:嵌套结构与自定义类型

import json
from datetime import datetime

# 嵌套结构示例
nested_data = {
    "user": {
        "id": 123,
        "profile": {
            "name": "Bob",
            "created_at": datetime.now()
        }
    },
    "logs": [
        {"timestamp": datetime.now(), "action": "login"},
        {"timestamp": datetime.now(), "action": "logout"}
    ]
}

# 自定义序列化器
class CustomEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, datetime):
            return obj.isoformat()
        return super().default(obj)

# 使用自定义编码器
json_str = json.dumps(nested_data, cls=CustomEncoder, indent=2)
print(json_str)

关键代码解释:

  • CustomEncoder 继承自 json.JSONEncoder
  • default() 方法处理未支持的类型(如 datetime)
  • cls 参数指定自定义编码器

输出示例:

{
  "user": {
    "id": 123,
    "profile": {
      "name": "Bob",
      "created_at": "2023-10-05T14:30:45.123456"
    }
  },
  "logs": [
    {
      "timestamp": "2023-10-05T14:30:45.123456",
      "action": "login"
    },
    {
      "timestamp": "2023-10-05T14:30:45.123456",
      "action": "logout"
    }
  ]
}

3. 文件读写操作

代码示例 3:文件持久化存储

import json

# 写入文件
data = {"key": "value", "numbers": [1, 2, 3]}
with open("data.json", "w", encoding="utf-8") as f:
    json.dump(data, f, indent=2, ensure_ascii=False)

# 读取文件
with open("data.json", "r", encoding="utf-8") as f:
    loaded_data = json.load(f)
print(loaded_data)

关键代码解释:

  • json.dump() 与 json.dumps() 区别:前者直接写入文件对象
  • ensure_ascii=False 保留非 ASCII 字符
  • 文件模式 w 会覆盖原有内容,a 为追加模式

五、完整案例

1. 用户信息管理系统

需求:实现用户信息的持久化存储和读取

实现步骤:

  1. 读取配置文件获取用户目录
  2. 将用户数据写入 JSON 文件
  3. 从 JSON 文件中读取用户数据
  4. 添加异常处理和日志记录

完整代码:

import json
import os
import logging

# 配置日志
logging.basicConfig(level=logging.INFO)

def save_user_data(user_id, data):
    """保存用户数据到 JSON 文件"""
    config_dir = "config"
    os.makedirs(config_dir, exist_ok=True)
    file_path = os.path.join(config_dir, f"user_{user_id}.json")
    
    try:
        with open(file_path, "w", encoding="utf-8") as f:
            json.dump(data, f, indent=2, ensure_ascii=False)
        logging.info(f"用户 {user_id} 数据已保存")
    except Exception as e:
        logging.error(f"保存用户 {user_id} 数据失败: {str(e)}")

def load_user_data(user_id):
    """从 JSON 文件读取用户数据"""
    config_dir = "config"
    file_path = os.path.join(config_dir, f"user_{user_id}.json")
    
    try:
        with open(file_path, "r", encoding="utf-8") as f:
            data = json.load(f)
        logging.info(f"用户 {user_id} 数据已加载")
        return data
    except FileNotFoundError:
        logging.warning(f"用户 {user_id} 文件不存在")
        return None
    except Exception as e:
        logging.error(f"加载用户 {user_id} 数据失败: {str(e)}")
        return None

# 示例使用
if __name__ == "__main__":
    user_data = {
        "user_id": 1001,
        "name": "Eve",
        "email": "eve@example.com",
        "preferences": {
            "theme": "dark",
            "language": "zh"
        },
        "session": {
            "token": "abc123",
            "timestamp": datetime.now().isoformat()
        }
    }
    
    save_user_data(1001, user_data)
    loaded = load_user_data(1001)
    print("加载的用户数据:", loaded)

关键点分析:

  • 使用 os.makedirs 确保目录存在
  • 异常处理覆盖文件操作的潜在错误
  • datetime.now().isoformat() 格式化时间戳
  • 文件路径使用动态构造,便于管理多个用户数据

六、源码解析

1. json.dumps 的实现原理

json.dumps() 实际调用的是 JSONEncoder 的 encode() 方法,其核心流程如下:

  1. 创建 JSONEncoder 实例
  2. 遍历对象属性,递归处理嵌套结构
  3. 对非标准类型调用 default() 方法
  4. 将 Python 对象转换为 JSON 字符串

代码片段:

class JSONEncoder:
    def encode(self, obj):
        # 实现序列化逻辑
        pass

2. json.loads 的实现原理

json.loads() 通过 JSONDecoder 解析 JSON 字符串,核心步骤包括:

  1. 构造 JSONDecoder 实例
  2. 逐字符解析字符串
  3. 构造 Python 对象
  4. 处理转义字符和特殊语法

代码片段:

class JSONDecoder:
    def decode(self, s):
        # 实现反序列化逻辑
        pass

七、进阶使用

1. 性能优化技巧

处理大数据量:

  • 使用 json.dump() 逐条写入,避免一次性加载全部数据
  • 使用 json.JSONDecoder 的 raw_decode() 方法直接解析字符串
  • 使用 ujson 库(第三方库)提升性能

示例:

import ujson

data = {"large": [i for i in range(100000)]}
json_str = ujson.dumps(data)

2. 安全增强

反序列化安全:

  • 避免反序列化不可信来源的数据
  • 使用 json.loads() 时添加类型检查
  • 限制解析深度和递归层数

示例:

def safe_load(json_str):
    return json.loads(json_str, object_hook=lambda d: d)  # 简化示例

3. 自定义类型支持

实现自定义序列化器:

class CustomDecoder(json.JSONDecoder):
    def decode(self, s):
        obj = super().decode(s)
        if isinstance(obj, dict):
            # 处理自定义类型
            if "__type__" in obj:
                type_name = obj["__type__"]
                if type_name == "MyCustomType":
                    # 构造自定义对象
                    return MyCustomType(**obj)
        return obj

八、性能与工程实践

1. 性能基准测试

操作类型数据量速度(秒)说明
小数据1000项0.001快速
中等数据10万项0.1可接受
大数据100万项1.5需优化

优化建议:

  • 使用 json.dumps() 预处理数据
  • 避免在循环中频繁调用 json.loads()
  • 对于极大数据,考虑使用二进制格式(如 MessagePack)

2. 异常处理规范

  • 文件不存在时使用 FileNotFoundError 捕获
  • 数据类型不匹配时使用 TypeError 捕获
  • 禁用 json.JSONDecodeError 异常处理(避免潜在安全风险)

3. 安全实践

  • 禁用 json.loads() 直接解析用户输入
  • 对敏感数据使用 json.dumps() 前进行加密
  • 对 JSON 数据进行完整性校验

九、常见问题与踩坑

1. 类型转换错误

错误示例:

data = {"a": 1, "b": [1, 2, 3]}
json_str = json.dumps(data)
print(json_str)  # 正常输出

错误场景:

data = {"a": datetime.now()}
json_str = json.dumps(data)  # 报错

解决方法:自定义 JSONEncoder 类型转换

2. 文件编码问题

错误示例:

with open("data.json", "r") as f:
    content = f.read()

问题:未指定编码可能导致乱码

解决方法:显式指定编码

with open("data.json", "r", encoding="utf-8") as f:
    content = f.read()

3. 高并发写入冲突

问题场景:多进程/线程同时写入同一文件时可能产生冲突

解决方法:

  • 使用文件锁(fcntl 模块)
  • 采用临时文件 + 原子写入
  • 使用数据库替代文件存储(如 SQLite)

十、最佳实践

1. 推荐使用场景

  • 配置文件存储(如 requirements.txt、setup.cfg)
  • 前后端数据通信(REST API 响应)
  • 日志记录(结构化日志格式)
  • 简单的持久化需求(非关键业务数据)

2. 不推荐使用场景

  • 关键业务数据存储(建议使用数据库)
  • 高并发写入场景(文件锁机制复杂)
  • 敏感数据存储(需加密处理)
  • 大规模数据传输(建议使用二进制格式)

3. 代码规范建议

  • 使用 json.dumps() 前进行数据验证
  • 使用 ensure_ascii=False 保留中文
  • 对于复杂结构使用 json.dumps() 预处理
  • 对于安全敏感场景禁用 json.loads() 直接解析

十一、总结

JSON 在 Python 中的读写操作看似简单,实则蕴含诸多细节。通过本文的深入分析,我们了解到:

  1. JSON 与 Python 数据类型的映射关系及限制
  2. 如何处理复杂结构和自定义类型
  3. 文件读写时的常见陷阱和解决方案
  4. 性能优化和安全增强的实践方法
  5. 在不同场景下的适用边界

在实际开发中,应根据具体需求选择合适的数据格式。对于轻量级数据交换和配置存储,JSON 是理想选择;但对于关键业务数据和高并发场景,建议结合数据库或其他持久化方案。掌握 JSON 的底层原理,不仅能提升代码质量,更能避免潜在的陷阱和风险。

评论已关闭

推荐阅读

AIGC实战——Transformer模型
2024年12月01日
Socket TCP 和 UDP 编程基础(Python)
2024年11月30日
python , tcp , udp
如何使用 ChatGPT 进行学术润色?你需要这些指令
2024年12月01日
AI
最新 Python 调用 OpenAi 详细教程实现问答、图像合成、图像理解、语音合成、语音识别(详细教程)
2024年11月24日
ChatGPT 和 DALL·E 2 配合生成故事绘本
2024年12月01日
omegaconf,一个超强的 Python 库!
2024年11月24日
【视觉AIGC识别】误差特征、人脸伪造检测、其他类型假图检测
2024年12月01日
[超级详细]如何在深度学习训练模型过程中使用 GPU 加速
2024年11月29日
Python 物理引擎pymunk最完整教程
2024年11月27日
MediaPipe 人体姿态与手指关键点检测教程
2024年11月27日
深入了解 Taipy:Python 打造 Web 应用的全面教程
2024年11月26日
基于Transformer的时间序列预测模型
2024年11月25日
Python在金融大数据分析中的AI应用(股价分析、量化交易)实战
2024年11月25日
AIGC Gradio系列学习教程之Components
2024年12月01日
Python3 `asyncio` — 异步 I/O,事件循环和并发工具
2024年11月30日
llama-factory SFT系列教程:大模型在自定义数据集 LoRA 训练与部署
2024年12月01日
Python 多线程和多进程用法
2024年11月24日
Python socket详解,全网最全教程
2024年11月27日
python之plot()和subplot()画图
2024年11月26日
理解 DALL·E 2、Stable Diffusion 和 Midjourney 工作原理
2024年12月01日