【JSON Python篇】通过代码操作 JSON
Python dict 和 JSON 字符串之间的双向转换dumps把字典倒出来变成字符串loads把字符串装回去变成字典。 【JSON基础篇】讲了 JSON 格式本身六种类型、五条规则、两种风格。这篇进入 Python看怎么用代码读写 JSON。 Python 的json模块是标准库的一部分不需要pip install直接import json就能用。一、核心就四个函数 先说名字。dump 的英文本意是倾倒、卸货像翻斗车把一车沙子哗地倒出来。在编程里取的就是这个意象把内存里的数据倒出来变成外部格式。反过来 load 就是装货把外部数据装回程序。 这个比喻在整个计算机领域都通用mysqldump 是把数据库倒出来core dump 是把内存倒出来tcpdump 是把网络包倒出来。json模块的 API 不少但日常开发只需要四个函数方向输入 → 输出场景json.dumps()Python → 字符串dict → str发给前端、存进 Redisjson.loads()字符串 → Pythonstr → dict解析请求体、读取存储json.dump()Python → 文件dict → file写配置文件json.load()文件 → Pythonfile → dict读配置文件 怎么记带s的处理字符串string不带s的处理文件file。 四个函数分两对dumps/loads处理字符串dump/load处理文件。上面是序列化Python → JSON下面是反序列化JSON → Python。二、dumps 和 loads最常用的一对2.1 把字典变成 JSON 字符串importjson user{name:zhangsan,age:28,skills:[Python,Flask]}textjson.dumps(user)print(fdict to json {text})print(ftype {type(text)})dict to json{name:zhangsan,age:28,skills:[Python,Flask]}typeclassstrjson.dumps的返回值是一个普通的 Python 字符串。字典变成了一段符合 JSON 格式的文本这个过程叫序列化serialization。2.2 把 JSON 字符串变回字典datajson.loads(text)print(fjson to dict {data})print(ftype {type(data)})json to dict{name:zhangsan,age:28,skills:[Python,Flask]}typeclassdictjson.loads做的是反方向的事。把 JSON 字符串解析回 Python 对象。这叫反序列化deserialization。 一来一回数据没有丢失。这是json模块最基本的用法程序里用字典传出去用字符串收回来再变字典。2.3 不只是字典dumps不只能处理字典列表也行json.dumps([1,2,3])# [1, 2, 3]json.dumps(hello)# hellojson.dumps(42)# 42json.dumps(True)# true ← 注意变成了小写json.dumps(None)# null ← 注意变成了 null 只要是 JSON 支持的类型都能转。不过实际开发中 99% 的情况都是在转字典或列表。三、dump 和 load读写文件 和dumps/loads逻辑完全一样只不过目标从字符串换成了文件3.1 写入文件importjson config{debug:True,port:5000,allowed_hosts:[localhost,127.0.0.1]}withopen(config.json,w)asf:json.dump(config,f,indent2) 生成的config.json{debug:true,port:5000,allowed_hosts:[localhost,127.0.0.1]} 注意两个自动转换Python 的True变成了 JSON 的true列表的格式也自动处理了。你不需要关心这些差异json模块全部搞定。3.2 读取文件withopen(config.json)asf:loadedjson.load(f)print(fport {loaded[port]})# 5000print(fdebug {loaded[debug]}, type {type(loaded[debug])})# class bool ← 自动转回 Python 的 Trueport 5000 debug True, type class bool 读回来之后类型也是对的true变回True数字还是数字列表还是列表。3.3 dump 和 dumps 的关系 其实dump内部就是先调dumps拿到字符串再写入文件。所以这两种写法效果一样# 方式一直接用 dumpwithopen(config.json,w)asf:json.dump(config,f,indent2)# 方式二先 dumps 再手动写withopen(config.json,w)asf:f.write(json.dumps(config,indent2)) 既然一样为什么要有dump因为处理大文件时dump可以边序列化边写入不需要先在内存里生成完整的字符串。日常用哪个都行但写文件用dump是更地道的写法。四、三个你一定会用到的参数dumps和dump都支持这些参数。4.1 indent让输出好看importjson user{name:zhangsan,age:28,skills:[Python,Flask]}# 默认紧凑一行print(默认输出格式)print(json.dumps(user))# 加了 indent2有缩进有换行print(美化后)print(json.dumps(user,indent2))indent的值是缩进的空格数。indent2就是每层缩进 2 个空格indent4就是 4 个。输出默认输出格式{name:zhangsan,age:28,skills:[Python,Flask]}美化后{name:zhangsan,age:28,skills:[Python,Flask]} 什么时候加给人看的时候加给机器看的时候别加。写配置文件、调试时 print 出来看加indent让人舒服。发给前端、存进 Redis不加多余的空格和换行只会浪费带宽和内存。4.2 ensure_ascii让中文正常显示data{城市:北京,人口:2100万}print(默认输出)print(json.dumps(data))print(设置中文直接输出)print(json.dumps(data,ensure_asciiFalse)) 默认情况下json.dumps会把**所有非 ASCII 字符转成\uXXXX转义序列。这在技术上完全没问题任何 JSON 解析器都能正确还原但人读起来很痛苦**。加上ensure_asciiFalse就能直接输出中文。输出默认输出{\u57ce\u5e02:\u5317\u4eac,\u4eba\u53e3:2100\u4e07}设置中文直接输出{城市:北京,人口:2100万} 写文件的时候记得同时指定编码withopen(data.json,w,encodingutf-8)asf:json.dump(data,f,ensure_asciiFalse,indent2) 写入的json文件如下{城市:北京,人口:2100万}4.3 separators控制紧凑程度payload{user_id:123,name:zhangsan}# 默认逗号后有空格冒号后有空格print(默认展示)print(json.dumps(payload))# 紧凑格式去掉所有多余空格print(紧凑格式)print(json.dumps(payload,separators(,,:)))separators接受一个元组(逗号分隔符, 冒号分隔符)。默认是(, , : )逗号和冒号后面各有一个空格改成(,, :)就把空格全去掉了。输出默认展示{user_id:123,name:zhangsan}紧凑格式{user_id:123,name:zhangsan} 这个参数日常用得不多但在需要精确控制输出格式的场景很关键。比如 JWT 的 Payload 编码就用紧凑格式,多一个空格Base64 编码结果就不一样签名就对不上。4.4 参数可以组合 这三个参数互不冲突可以一起用data{姓名:张三,年龄:28}print(json.dumps(data,indent2,ensure_asciiFalse)){姓名:张三,年龄:28} 不过indent和separators(,, :)一般不同时用加了缩进就是为了好看再去空格就矛盾了。五、类型映射Python 和 JSON 不是一一对应的 这是用json模块最容易踩坑的地方。序列化和反序列化时Python 类型和 JSON 类型之间有一张固定的映射表Python 类型→ JSON 类型→ 读回来的 Python 类型dictobject{}dictlistarray[]listtuplearray[]list←回不去了strstringstrintnumberintfloatnumberfloatTrue/Falsetrue/falseTrue/FalseNonenullNone 大部分能完美往返但注意有两个坑。5.1 坑一tuple 转出去就回不来了 JSON 里没有 tuple 这个概念只有 array。所以 tuple 序列化后和 list 长得一样读回来自然也是 list。如果你的代码依赖 tuple 的不可变性来回转换后会出问题。tuple类型data{coords:(10,20)}textjson.dumps(data)print(text){coords:[10,20]}从JSON读回loadedjson.loads(text)print(type(loaded[coords]))classlist5.2 坑二整数键会被悄悄转成字符串 JSON 规范要求键必须是字符串所以dumps会自动把整数键转成字符串。但loads读回来不会帮你转回整数它只按 JSON 规范来键就是字符串。importjson data{1:one,2:two}textjson.dumps(data)print(dict to json)print(text)dict tojson{coords:[10,20]}如果你的字典用整数做键来回转换后会对不上。loadedjson.loads(text)print(json to dict)print(loaded[1])json todictTraceback(most recent call last): File/02_JSON用法Python篇/code/scripts/integer_demo.py, line11,inmoduleprint(loaded[1])KeyError:15.3 不在表里的类型直接报错datetime、set、bytes、自定义对象都不在映射表里dumps遇到它们就直接抛TypeError。fromdatetimeimportdatetime json.dumps({time:datetime.now()})# TypeError: Object of type datetime is not JSON serializable(1) 手动转格式 最简单的办法是手动转fromdatetimeimportdatetime,timezone data{event:login,time:datetime.now(timezone.utc).isoformat(),# datetime → 字符串tags:list({1,2,3}),# set → list}print(json.dumps(data)){event:login,time:2026-03-26T03:50:03.94689500:00,tags:[1,2,3]} datetime 转成 ISO 格式字符串是业界惯例前端拿到后可以直接new Date()解析。(2) 使用default指定处理方法 如果项目里到处都有 datetime每次手动转太烦可以用default参数写一个统一的转换函数defjson_default(obj):json.dumps 遇到不认识的类型时会调用这个函数ifisinstance(obj,datetime):returnobj.isoformat()ifisinstance(obj,set):returnlist(obj)raiseTypeError(fObject of type{type(obj).__name__}is not JSON serializable)default参数接受一个函数每当dumps遇到映射表里没有的类型就把那个对象丢给这个函数函数返回什么 JSON 里就写什么。写一次到处复用。data{time:datetime.now(timezone.utc),tags:{1,2,3}}print(json.dumps(data,defaultjson_default))输出{time:2026-03-26T03:53:23.26522700:00,tags:[1,2,3]}六、错误处理两种常见异常 用json模块基本只会遇到以下两种错误。6.1 序列化失败TypeError 把不支持的类型丢给dumpsimportjsonfromdatetimeimportdatetimetry:json.dumps({time:datetime.now()})exceptTypeErrorase:print(e)# Object of type datetime is not JSON serializable 解决办法上面讲过了手动转类型或用default参数。6.2 反序列化失败JSONDecodeError 把不合法的 JSON 字符串丢给loadstry:json.loads({name: zhangsan})# 单引号不是合法 JSONexceptjson.JSONDecodeErrorase:print(e)# Expecting property name enclosed in double quotestry:json.loads()# 空字符串exceptjson.JSONDecodeErrorase:print(e)# Expecting valueJSONDecodeError的报错信息通常很明确会告诉你第几行第几个字符出了问题。常见原因单引号代替了双引号、尾逗号、缺少引号、字符串没转义。七、总结你想做的事用什么字典 → JSON 字符串json.dumps(data)JSON 字符串 → 字典json.loads(text)字典 → JSON 文件json.dump(data, f)JSON 文件 → 字典json.load(f)输出好看indent2中文不转义ensure_asciiFalse紧凑无空格separators(,, :)datetime / set 报错手动转或用default参数解析报错捕获json.JSONDecodeErrorjson模块的本质就是Python 对象和 JSON 文本之间的翻译器。四个核心函数、三个常用参数、一张类型映射表、两种异常。记住这些单独用json模块就没有盲区了。