JSON 是当今世界最常用的数据交换格式——API 返回、配置文件、日志、数据库字段……几乎无处不在。Python 内置的 json 模块非常够用。这一篇讲清楚 json 模块的四个核心函数、编码/解码规则、自定义序列化、以及处理”日期时间”、”Decimal”、”自定义类”这些默认不支持的类型 的技巧。
一、四个核心函数 1 2 3 4 5 6 7 8 9 10 11 import jsonjson.dumps(obj) json.dump(obj, f) json.loads(s) json.load(f)
记忆 :带 s 的是 str 版本,不带 s 的操作文件。
二、类型映射 Python ↔ JSON 类型对应关系:
Python
JSON
dict
object
list, tuple
array
str
string
int, float
number
True / False
true / false
None
null
JSON 不支持的常见类型 :
datetime / date
Decimal
set / frozenset
bytes
自定义类
以上都会引发 TypeError,需要自己转换。
三、序列化的常用参数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 data = {"name" : "咖飞" , "age" : 3 , "tags" : ["python" , "学习" ]} json.dumps(data) json.dumps(data, ensure_ascii=False ) json.dumps(data, ensure_ascii=False , indent=2 ) json.dumps(data, ensure_ascii=False , sort_keys=True ) json.dumps(data, separators=("," , ":" ))
几乎所有场景都建议 ensure_ascii=False ——除非你的下游只能处理 ASCII。
四、读写文件 1 2 3 4 5 6 7 with open ("data.json" , "w" , encoding="utf-8" ) as f: json.dump(data, f, ensure_ascii=False , indent=2 ) with open ("data.json" , encoding="utf-8" ) as f: data = json.load(f)
五、常见错误 编码错 1 2 3 4 5 json.dumps({datetime.now(): "x" }) json.dumps({1 , 2 , 3 })
解码错 1 2 3 4 5 6 7 8 json.loads("{'a': 1}" ) json.loads("undefined" ) json.loads(open ("data.json" ).read())
六、处理”不支持”的类型 方法 1:自定义 default 函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 from datetime import datetime, datefrom decimal import Decimaldef json_default (obj ): if isinstance (obj, (datetime, date)): return obj.isoformat() if isinstance (obj, Decimal): return str (obj) if isinstance (obj, set ): return list (obj) raise TypeError(f"不支持的类型:{type (obj)} " ) data = { "time" : datetime.now(), "amount" : Decimal("3.14" ), "tags" : {"python" , "json" }, } print (json.dumps(data, default=json_default, ensure_ascii=False ))
方法 2:继承 JSONEncoder 1 2 3 4 5 6 7 8 9 10 class MyEncoder (json.JSONEncoder): def default (self, obj ): if isinstance (obj, (datetime, date)): return obj.isoformat() if isinstance (obj, Decimal): return str (obj) return super ().default(obj) print (json.dumps(data, cls=MyEncoder))
两种方法功能等价,看喜好。
七、反序列化自定义处理 object_hook 让你在解析每个字典时干预:
1 2 3 4 5 6 7 8 9 10 11 12 def parse_datetime (d ): for k, v in d.items(): if isinstance (v, str ) and len (v) >= 10 and v[4 ] == "-" : try : d[k] = datetime.fromisoformat(v) except ValueError: pass return d json.loads('{"time": "2025-09-26T10:12:47"}' , object_hook=parse_datetime)
八、dataclass 与 JSON 如果你的数据是 dataclass,序列化很简单:
1 2 3 4 5 6 7 8 9 10 11 12 from dataclasses import dataclass, asdictimport json@dataclass class User : name: str age: int u = User("咖飞" , 3 ) json.dumps(asdict(u), ensure_ascii=False )
反序列化:
注意 :字段类型不一致(比如 JSON 里 age 是字符串),要手动转。用第三方 pydantic / msgspec 能优雅解决这个。
九、pydantic 简介 pydantic 是 Python 里最强大的”JSON ↔ 类”工具。安装:pip install pydantic。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 from pydantic import BaseModelfrom datetime import datetimeclass User (BaseModel ): name: str age: int joined: datetime u = User.model_validate_json('{"name": "咖飞", "age": 3, "joined": "2025-09-26T10:00:00"}' ) print (u.joined) print (u.model_dump_json(indent=2 ))
pydantic 处理 datetime、UUID、Decimal、枚举、可选字段、嵌套模型都很自然。API 项目(FastAPI)里几乎必用。
十、JSON Lines:一行一个 JSON 大数据场景常用 JSON Lines(.jsonl):每行是一个独立的 JSON ,用换行分隔。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 with open ("events.jsonl" , "w" , encoding="utf-8" ) as f: for e in events: f.write(json.dumps(e, ensure_ascii=False ) + "\n" ) def read_jsonl (path ): with open (path, encoding="utf-8" ) as f: for line in f: line = line.strip() if line: yield json.loads(line) for event in read_jsonl("events.jsonl" ): process(event)
十一、性能提示
json 是 Python 自带的纯 Python + C 加速 实现,够快;
更快的替代:orjson(Rust 写,2-5 倍快)、ujson;
处理超大 JSON 用 流式解析 (ijson 库),避免一次读进内存。
orjson 用法:
1 2 3 4 5 import orjsonb = orjson.dumps({"a" : 1 , "time" : datetime.now()}) d = orjson.loads(b)
orjson 原生支持 datetime、UUID、dataclass、numpy。
十二、安全性 不要用 eval 解析 JSON :
1 2 3 4 5 data = eval (json_str) data = json.loads(json_str)
json.loads 只解析纯数据,不会执行代码。
十三、schema 校验 生产 API 需要严格的 schema:
jsonschema :标准 JSON Schema 校验;
pydantic :类型注解驱动的模型;
msgspec :极快的替代。
例子(pydantic):
1 2 3 4 5 6 7 8 9 10 11 class Order (BaseModel ): id : int amount: float status: Literal ["pending" , "paid" , "shipped" ] Order.model_validate({"id" : 1 , "amount" : 99.9 , "status" : "paid" }) Order.model_validate({"id" : "x" , "amount" : 99.9 , "status" : "unknown" })
十四、一个完整例子:配置文件加载器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 """一个 JSON 配置文件加载器,支持默认值、环境变量覆盖、类型转换。""" import jsonimport osfrom pathlib import Pathfrom datetime import datetimefrom dataclasses import dataclass, field, asdictDEFAULT_CONFIG = { "app_name" : "咖飞助手" , "debug" : False , "port" : 8080 , "features" : {"chat" : True , "search" : False }, "started_at" : datetime.now().isoformat(), } def load_config (path: Path ) -> dict : """加载配置文件;不存在则用默认。""" if path.exists(): config = json.loads(path.read_text(encoding="utf-8" )) else : config = DEFAULT_CONFIG.copy() save_config(path, config) for key, value in config.items(): env_key = f"APP_{key.upper()} " if env_key in os.environ: raw = os.environ[env_key] if isinstance (value, bool ): config[key] = raw.lower() in ("1" , "true" , "yes" ) elif isinstance (value, int ): config[key] = int (raw) else : config[key] = raw return config def save_config (path: Path, config: dict ) -> None : path.parent.mkdir(parents=True , exist_ok=True ) path.write_text( json.dumps(config, ensure_ascii=False , indent=2 ), encoding="utf-8" , ) if __name__ == "__main__" : cfg = load_config(Path("config.json" )) print (cfg)
十五、常见陷阱
忘 ensure_ascii=False :中文变 \uXXXX。
JSON 只支持双引号 :单引号 JSON 用 ast.literal_eval 或改数据。
JSON 数字精度 :json.loads('{"x": 3.14e400}') 会转 inf;大整数没问题。
NaN / Infinity :Python json.dumps(float('nan')) 输出 NaN(非标准 JSON!)。用 allow_nan=False 让它抛错。
序列化 datetime 报错 :写 default 函数或 dataclass + asdict。
文件 BOM :用 encoding="utf-8-sig"。
JSON key 必须是字符串 :{1: 'x'} 序列化会把 int 转字符串(危险,反序列化后是字符串了)。
十六、小结与延伸阅读
四大 API:dumps / loads / dump / load;
永远 ensure_ascii=False;
想美化用 indent=2;
不支持的类型用 default= 函数或 JSONEncoder 子类;
反序列化自定义用 object_hook;
高性能用 orjson;
类型安全用 pydantic;
大数据流用 JSON Lines + 生成器。
延伸阅读:
下一篇 常用标准库:os、sys、pathlib 我们讲文件系统与运行环境的工具箱。