Python json.dumps 报 Object of type set is not JSON serializable
json.dumps 遇到 set 类型报错,可能是变量外多写了花括号。区分字典、集合与 JSON 文本,按字段协议修正构造方式,并验证输出的类型和嵌套结构。
直接把字典写进 json.dumps() 能成功,提取到变量后却报 TypeError: Object of type set is not JSON serializable,常见原因是变量外面多套了一层花括号。外层对象即使是字典,某个嵌套值也可能已经变成集合。
本文针对 Python 3 标准库 json。自定义编码器或框架的 JSON provider 可能另外支持某些类型,排查时先确认实际调用的序列化实现。
花括号不会把字符串内容解释成键值对
以下写法中,detail 是字符串,而 payload["detail"] 是集合:
import json
detail = "'message': 'version missing'"
payload = {"detail": {detail}}
json.dumps(payload)
{"message": "version missing"} 包含键和值,构造字典;{detail} 只有一个表达式,构造含一个元素的集合。字符串里看起来像冒号、引号的内容,不会再次被当成 Python 字典语法解释。
在失败的构造位置检查对象类型即可缩小范围:
print(type(detail).__name__)
print(type(payload["detail"]).__name__)
对上面的示例,预期分别为 str 和 set。不需要把整个业务对象连同令牌、个人信息一起打印出来。更深的嵌套结构,应沿构造链确认出错字段的实际类型。
字段需要 JSON 对象时,保留字典本身
让变量从一开始就保存结构化数据,并直接作为字段值:
import json
detail = {"message": "version missing"}
payload = {"detail": detail}
encoded = json.dumps(payload, ensure_ascii=False)
decoded = json.loads(encoded)
print(decoded["detail"]["message"])
这个例子应能按键读取 message。不要再写成 {"detail": {detail}}:当 detail 已经是字典时,后者是在尝试把不可哈希的字典放进集合,可能更早报 unhashable type: 'dict'。
ensure_ascii=False 只影响非 ASCII 字符的输出形式,不会让集合自动变成 JSON 对象。类似地,skipkeys=True 针对不受支持的字典键,不能修复作为字段值出现的集合。
输入确实是 JSON 文本时,先解析一次
如果上游提供的是合法 JSON 文本,应在边界处解析,而不是通过花括号“包装”:
import json
raw_detail = '{"message": "version missing"}'
detail = json.loads(raw_detail)
if not isinstance(detail, dict):
raise TypeError("detail must be a JSON object")
encoded = json.dumps({"detail": detail})
这里的输入必须符合 JSON 语法。"'message': 'version missing'" 既缺少对象花括号,又使用了单引号,不是合法的 JSON 对象文本。不要用 eval() 解析外部输入,也不要靠全局替换引号修补内容;字符串内部可能本来就包含合法的引号。
如果字段协议本来要求字符串,则直接传字符串即可。字段最终采用对象、数组还是字符串,应遵守接口约定。
业务确实需要集合时,明确转换为数组
JSON 没有 Python 集合类型。若字段表示去重后的字符串标签,可以明确约定输出数组:
import json
tags = {"python", "json"}
payload = {"tags": sorted(tags)}
encoded = json.dumps(payload)
这里使用 sorted() 的前提是元素都是可相互比较的字符串,用于得到明确顺序。任意混合类型集合不一定能排序;转成数组之后,接收端也不会自动恢复集合的去重语义。
给整个编码器统一添加 default=str,可能把错误数据悄悄变成字符串;统一 default=list 也会改变其他未知类型的处理方式。对原本应是对象的字段,这两种做法都可能掩盖构造错误。
验证结构,不只看编码是否成功
修复后将输出解码回来,核对顶层和关键字段类型、字段名及取值。如果接口需要对象,就验证能按预定键访问,而不是只检查字符串中出现了 message。
对集合转数组的改动,还需检查下游是否依赖顺序、重复元素或集合语义。上线前保留旧版字段构造代码及接口样例;若下游不兼容,应回退这次结构变更并重新协商字段协议,不能用字符串兜底把异常隐藏起来。


