本文要点
这是初学者最容易困惑的一处
JSON和Python在布尔值与空值上的写法不同,这是loads和dumps转换时最需要记住的对照关系
JSON对象的键在规范里始终是字符串类型

本文目录
先分清:一个是文本,一个是内存里的对象
如果你手里有一段花括号包起来的内容,先要判断它是JSON字符串还是Python字典,因为两者的操作方式完全不同。
JSON是一种轻量级数据交换格式,由RFC 7159和ECMA-404规定。在Python里,JSON文本表现为str(字符串),字典表现为dict(字典对象)。字符串是文本,你可以打印、拼接、写入文件;字典是内存中的对象,你可以用键取值、增删键值对。
转换方向只有两个:
- json.loads(s):把JSON字符串解析成Python对象。
- json.dumps(obj):把Python对象序列化成JSON字符串。
本文只讨论内存中JSON字符串和字典的互转,不涉及文件读取。下面所有示例都是自建的练习数据,不是对真实数据的测试记录。
一个容易混淆的点:JSON里的object指花括号包起来的数据,类似Python字典;但Python里所有值都是对象,两个“对象”含义不同。
中文字符串:dumps默认转义,loads自动还原
这是初学者最容易困惑的一处。用dumps转换含中文的字典时,默认输出会把非ASCII字符转义成\uXXXX形式。
import json
d = {"name": "示例项目", "city": "北京"}
s = json.dumps(d)
print(s)输出里中文会变成转义序列,因为ensure_ascii默认为True,输出保证只含可打印ASCII字符。如果你希望中文原样显示,可以设置ensure_ascii=False:
s2 = json.dumps(d, ensure_ascii=False)
print(s2)这次输出里中文保持原样。
反向操作时,loads能正确还原这两种写法:
json.loads(s) # 转义写法
json.loads(s2) # 原样写法两者解析结果相同。也就是说,转义只是文本表示方式,不影响解析后的内容。
需要提醒:ensure_ascii=False时,除引号、反斜杠和U+0000到U+001F控制字符外,其余字符原样输出。
布尔值和null:JSON小写,Python首字母大写
JSON和Python在布尔值与空值上的写法不同,这是loads和dumps转换时最需要记住的对照关系。
按json模块的解码转换表:
- JSON的true对应Python的True
- JSON的false对应Python的False
- JSON的null对应Python的None
编码方向则相反:Python的True、False、None分别输出为true、false、null。
自建示例:
import json
s = '{"active": true, "deleted": false, "note": null}'
d = json.loads(s)
print(d)解析后d里的值分别是True、False、None。再转回去:
print(json.dumps(d))输出里又变回true、false、null。
关键点:JSON文本里不能写True、False、None,Python字典里也不该把true、false、null当变量名。写错方向会直接报错。
字典键会变成字符串:往返转换可能不相等
JSON对象的键在规范里始终是字符串类型。当Python字典转成JSON时,所有键都会被转成字符串。
这带来一个后果:如果原字典用了非字符串键,转换后再转回来,结果可能和原字典不相等。文档明确写了这一点:loads(dumps(x)) != x,当x含非字符串键时成立。
自建示例:
import json
x = {1: "a", 2: "b"}
s = json.dumps(x)
print(s)
print(json.loads(s))转回后键变成字符串"1"、"2",和原来的整数键1、2不相等。
另外,sort_keys=True会先排序键再转成字符串,所以数字键按数值排序,而不是按字符串表示排序。
格式错误怎么定位:用JSONDecodeError的三个属性
loads遇到不合法的JSON文档时,会抛出JSONDecodeError,它是ValueError的子类。
这个异常带有几个属性,能帮你定位问题:
- msg:未格式化的错误消息
- doc:正在解析的JSON文档
- pos:解析失败的起始索引
- lineno:pos对应的行号
- colno:pos对应的列号
自建示例,故意写一个格式错误的字符串:
import json
bad = '{"name": "示例", }'
try:
json.loads(bad)
except json.JSONDecodeError as e:
print(e.msg)
print(e.lineno, e.colno)这里末尾多了一个逗号,属于常见错误。异常会告诉你出错的位置。
其他常见格式错误包括:用单引号代替双引号、键没有加引号、括号不配对。这些都会触发JSONDecodeError。
两个容易踩的边界:重复键和特殊浮点值
这两点资料里有明确说明,初学者容易忽略。
第一,重复键。RFC规定JSON对象的名称应当唯一,但没规定重复时怎么处理。默认情况下,json模块不报错,只保留最后一个键值对:
import json
weird = '{"x": 1, "x": 2, "x": 3}'
print(json.loads(weird))结果是{'x': 3}。如果你需要改变这个行为,可以用object_pairs_hook参数。
第二,Infinity和NaN。RFC不允许表示无穷或NaN数值,但默认情况下json模块接受并输出它们:
import json
print(json.dumps(float('nan')))
print(json.loads('NaN'))输出分别是NaN和nan。这些结果不是合法JSON。序列化时可以用allow_nan=False改为抛ValueError;反序列化时可以用parse_constant参数改变行为。
这两点说明:json模块默认设置下并不严格符合RFC,实现了一些JavaScript合法但JSON不合法的扩展。
安全提醒与适用范围
解析来自不可信来源的JSON数据时要谨慎。文档警告:恶意JSON字符串可能导致解码器消耗大量CPU和内存资源,建议限制待解析数据的大小。
本文只讨论内存中JSON字符串和字典的互转,不涉及文件读取、网络请求或数据持久化。所有示例均为自建练习数据,不是实测记录。
最后一点:本文的对照表和示例是编辑整理的,用于帮助理解转换关系,不替代官方文档。具体参数行为以你所使用Python版本的官方文档为准。
观看知识要点视频 18 秒
18秒无旁白要点导览,文字来自已审核正文;完整步骤、条件及参考资料见下方。
参考资料
以下原始资料提供本文依据;请结合日期、适用条件和产品说明查阅。
请结合原始资料的适用条件阅读。
返回表格与数据 →