💡 开发中经常要存点临时配置、爬虫结果或者API响应缓存?用数据库太重,用普通文本手动解析又麻烦?今天的主角——Python标准库json,再加点第三方小工具,轻松搞定轻量级JSON文本存储与交互~

Python 中的 JSON 处理教程

JSON (JavaScript Object Notation) 是一种轻量级的数据交换格式,人好读机器也好解析,现在几乎是全场景通用的“通用语言”。本教程从基础到进阶,带你快速掌握Python中的JSON处理。


📌 JSON 基础

JSON 数据结构

JSON 只支持6种核心数据结构,非常简洁:

  • 对象 🗂️:花括号 {} 包裹的键值对,键必须是双引号字符串
  • 数组 📊:方括号 [] 包裹的有序值集合,可混合类型(但建议规范)
  • :可以是字符串(双引号)、数字、布尔值(true/false)、null、对象或数组

🔄 JSON 与 Python 类型对应关系

Python的json模块会自动做双向映射,记清楚这张表就行:

JSON 类型Python 类型
objectdict
arraylist
stringstr
numberint/float
trueTrue
falseFalse
nullNone

📖 读取 JSON 数据

从字符串加载(json.loads

loads = load string,适合处理API返回的字符串、多行配置文本等。

import json

# 多行JSON字符串可以用三引号,合法JSON不强制缩进,但Python写好看点
json_str = '''
{
    "name": "John",
    "age": 30,
    "city": "New York",
    "hobbies": ["reading", "traveling"],
    "married": false
}
'''

# 转换为Python对象
data = json.loads(json_str)

print(type(data))  # <class 'dict'>
print(data["name"])  # John
print(data["hobbies"][0])  # reading

从文件加载(json.load

load = load file object,记得with语句自动关闭文件。

import json

# 假设同目录下有data.json文件
try:
    with open('data.json', 'r', encoding='utf-8') as f:
        data = json.load(f)  # 直接传文件对象
except FileNotFoundError:
    print("文件不存在,创建默认数据...")
    data = {"default": True}

print(data)

✍️ 写入 JSON 数据

转为JSON字符串(json.dumps

dumps = dump string,三个必用的实用参数要记牢:

  1. ensure_ascii=False:保留中文等非ASCII字符(不加会变成\uXXXX乱码)
  2. indent=24:设置缩进层级,方便人阅读
  3. sort_keys=True:按字典键排序,输出结果更稳定(便于比较)
import json

data = {
    "name": "张三",
    "age": 28,
    "city": "北京",
    "hobbies": ["摄影", "编程"],
    "married": True
}

# 转换为带缩进、有序、保留中文的JSON字符串
json_str = json.dumps(data, ensure_ascii=False, indent=4, sort_keys=True)
print(json_str)

写入JSON文件(json.dump

dump = dump file object,参数和dumps一致。

import json

data = {
    "name": "李四",
    "age": 35,
    "city": "上海",
    "hobbies": ["游泳", "音乐"],
    "married": False
}

with open('output.json', 'w', encoding='utf-8') as f:
    json.dump(data, f, ensure_ascii=False, indent=4, sort_keys=True)

🚀 高级用法

处理自定义类/日期时间对象

标准的json模块无法直接序列化自定义类(如User)或datetime,可以用两种方法:

方法1:自定义default函数(简单灵活)

dumps/dump时传入default,告诉模块怎么处理不认识的对象:

import json
from datetime import datetime

class User:
    def __init__(self, name, age):
        self.name = name
        self.age = age
        self.created_at = datetime.now()

def custom_encoder(obj):
    # 先处理特殊类型
    if isinstance(obj, datetime):
        # 转ISO格式字符串,跨平台且可读
        return obj.isoformat()
    if isinstance(obj, User):
        # 显式指定要序列化的字段(比直接用__dict__安全,不会带临时属性)
        return {
            "name": obj.name,
            "age": obj.age,
            "created_at": obj.created_at
        }
    # 其他类型交给默认处理(会抛出TypeError)
    raise TypeError(f"Object of type {obj.__class__.__name__} not serializable")

user = User("王五", 40)
json_str = json.dumps(user, default=custom_encoder, ensure_ascii=False, indent=2)
print(json_str)

方法2:继承json.JSONEncoder(适合封装)

如果多个地方要用到同样的序列化逻辑,可以封装成子类,传入cls参数:

class CustomJSONEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, datetime):
            return obj.isoformat()
        if isinstance(obj, User):
            return {
                "name": obj.name,
                "age": obj.age,
                "created_at": obj.created_at
            }
        return super().default(obj)

# 调用时传cls
json_str = json.dumps(user, cls=CustomJSONEncoder, ensure_ascii=False, indent=2)

解析时自动还原特殊类型(object_hook

读取JSON时,每解析到一个JSON对象(即Python的dict),都会调用object_hook函数,我们可以在这里把ISO格式的时间还原成datetime,或者把普通dict还原成User

import json
from datetime import datetime

def custom_decoder(dct):
    # 每解析到一个dict就检查
    if "created_at" in dct:
        try:
            dct["created_at"] = datetime.fromisoformat(dct["created_at"])
        except ValueError:
            pass  # 如果格式不对就保留字符串
    # 如果有name和age,还可以还原成User(可选)
    if "name" in dct and "age" in dct and "created_at" in dct:
        return User(dct["name"], dct["age"])  # 注意这里要把created_at单独处理下
    return dct

json_str = '''
{
    "name": "赵六",
    "age": 45,
    "created_at": "2023-01-15T10:30:00"
}
'''

data = json.loads(json_str, object_hook=custom_decoder)
print(type(data))  # <class '__main__.User'>(如果上面做了还原)
print(type(data["created_at"]) if isinstance(data, dict) else type(data.created_at))  # <class 'datetime.datetime'>

流式处理大型JSON文件(用ijson

如果JSON文件超过内存限制(比如几个G的日志或爬虫数据),可以用第三方库ijson逐块读取,不用一次性加载整个文件:

# 先安装ijson
pip install ijson
import ijson

# 假设large_users.json是一个包含很多用户的数组:[{"name":...}, {"name":...}, ...]
with open('large_users.json', 'rb') as f:
    # 只流式读取并处理所有用户名,不用加载整个数组
    for name in ijson.items(f, 'item.name'):
        print(name)
        # 这里可以做插入数据库、统计等操作

✅ 最佳实践

  1. 编码问题必注意:读写文件加encoding='utf-8',序列化加ensure_ascii=False
  2. 文件操作必用with:自动管理文件句柄,防止泄漏
  3. 错误处理必加:至少捕获FileNotFoundError(文件不存在)、json.JSONDecodeError(解析错误,Python3.5+)、TypeError(序列化错误)
  4. 大型数据必用流式ijsonpandas.read_json(chunksize=...)都是好选择
  5. 安全性必考虑:不要解析来自不可信源的JSON,防止恶意代码注入(虽然Python的json模块比eval安全,但还是要谨慎)

🎉 总结

Python的标准库json已经能覆盖90%的日常JSON处理需求,搭配ijson等小工具,轻量级文本存储、API交互、临时数据缓存都能轻松搞定~下次碰到这类需求,别再急着开数据库啦!