python序列化和反序列化_json与pickle库对比及使用场景解析

5次阅读

json.dumps()输出跨语言的可读json字符串,仅支持7种基础类型;pickle.dumps()输出python专用二进制流,支持几乎所有对象但有反序列化安全风险且不跨版本兼容。

python序列化和反序列化_json与pickle库对比及使用场景解析

json.dumps() 和 pickle.dumps() 的核心差异在哪

根本区别不在“能不能转”,而在“转成什么、给谁用”。json.dumps() 输出标准 JSON 字符串,人类可读、跨语言通用;pickle.dumps() 输出 Python 专用二进制字节流,只保证在同版本 Python 中能还原原对象,不兼容其他语言,也不保证跨 Python 版本安全。

常见错误现象:TypeError: Object of type set is not JSON serializable —— 这说明你试图用 json.dumps() 序列化 setdatetime、自定义类实例等非基础类型,而 pickle 默认就能处理这些(除非对象含不可持久化的状态,如文件句柄)。

  • json 只支持 dictliststrintFloatboolNone 这七种类型及其嵌套
  • pickle 支持几乎所有 Python 对象,但反序列化时会执行任意代码(__reduce__ 等钩子),有严重安全风险,绝不能加载不可信数据
  • 性能上,pickle 通常比 json 快 2–5 倍(尤其对复杂嵌套结构),但生成的字节流体积更大

什么时候必须用 json,什么时候只能用 pickle

json 不是“更安全”的权衡,而是场景硬性要求:只要数据要出 Python 进浏览器、进 node.js、进 java 后端、进数据库的 JSON 字段(如 postgresqlJSONB)、或需要人工调试/审查内容,就必须用 json

pickle 是“别无选择”的妥协:比如缓存一个带方法绑定的 threading.Lock、保存 numpy.ndarray(虽有更优方案如 np.save)、或快速暂存训练中的 sklearn 模型到磁盘供同一脚本下次加载——此时你不需要跨语言,也不关心内容是否可读,只求快且保真。

立即学习Python免费学习笔记(深入)”;

  • Web API 响应体、前端 localStorage 存储、配置文件 → 强制用 json
  • redis 缓存 Python 函数闭包、临时保存 torch.nn.Module 实例 → 只能用 pickle(但建议优先考虑 torch.save 等专用方案)
  • 日志中记录结构化事件?用 json;记录调试用的完整帧对象?pickle 是唯一现实选项(尽管不推荐)

json 处理 datetime 或自定义类的绕不过去的坑

json.dumps()TypeError: Object of type datetime is not JSON serializable 是高频问题,不是 bug,是设计使然。它不提供默认转换逻辑,必须显式干预。

最简方案是传 default 参数:

import json from datetime import datetime 

data = {"time": datetime.now(), "value": 42} json.dumps(data, default=str) # → '{"time": "2024-06-12T15:23:45.123456", "value": 42}'

default=str 是“暴力转字符串”,丢失类型信息,反序列化时得靠业务逻辑再解析。更严谨的做法是定义专用 encoder:

  • 继承 json.JSONEncoder,重写 default() 方法,对 datetime 返回 isoformat(),对 bytes 返回 base64.b64encode(...).decode()
  • 反序列化时,用 object_hook 回调识别特定 key(如 "__datetime__")并还原为 datetime
  • 不要试图让 json 原生支持 set:序列化前转 list,反序列化后转回 set,这是最清晰可控的方式

pickle.load() 加载不可信数据等于执行远程代码

这是被反复警告却仍被踩爆的致命点。pickle 反序列化过程会动态调用类构造器、__setstate__、甚至 __reduce__ 返回的任意可调用对象。攻击者可构造恶意字节流,在 pickle.load() 时触发 os.system("rm -rf /")

没有“安全模式”开关。所谓 safe=True 是不存在的 API。唯一防御手段是:绝不加载来源不明的 pickle 数据。

  • 如果你控制数据生产端和消费端,且确定永远只在可信环境(如单机离线脚本)中使用,pickle 可用
  • 任何涉及网络传输、用户上传、第三方 SDK 返回的数据,一律禁止 pickle.load()
  • 替代方案:用 json + 显式类型转换;或用 msgpack(更快更小,但仍是 Python-centric,且同样不解决安全问题);真正需要高性能+安全时,选 protobufcapnproto

复杂点在于:很多人以为“我只用 pickle 存自己代码里的对象,肯定安全”,却忽略了依赖库可能悄悄把用户输入塞进某个会被 pickle 序列化的容器里——这种隐式污染极难审计。

text=ZqhQzanResources