JSONDecodeError根本原因是输入文本违反RFC 8259规范,常见触发场景包括BOM头、末尾逗号、单引号、注释、Unicode转义不全、换行符混用;Python json模块无strict=False参数,严格解析不可绕过;推荐方案为预清洗后标准解析,次选ast.literal_eval(限可信Python字面量),或引入json5/demjson3第三方库;try-except需记录e.pos位置并针对性修复,注意e.pos基于解码后字符串。
JSONDecodeError常见触发场景和根本原因
Python的json.loads()或json.load()抛出JSONDecodeError,本质是输入文本不符合RFC 8259 JSON语法规范。最常踩的坑不是“数据错了”,而是:字符串含BOM头、末尾多逗号、单引号代替双引号、注释(//或/* */)、Unicode转义不完整、换行符混用(\r\n vs \n)。这些在浏览器开发者工具里看着像JSON,但标准解析器直接拒收。
用json.loads()加strict=False?不行,JSON模块根本不支持该参数
很多人搜“非严格模式”会误以为json.loads(s, strict=False)能绕过校验——实际这个参数不存在,传了会报TypeError: loads() got an unexpected keyword argument 'strict'。Python标准库json模块始终坚持严格解析,不提供宽容选项。想“非严格”,必须换工具或预处理。
真正可行的三类解决方案
按优先级排序:
Find JSON Path Online
Easily find JSON paths within JSON objects using our intuitive Json Path Finder
-
预清洗 + 标准解析(推荐):用正则或字符串操作修复典型问题,再交给
json.loads()。例如去掉BOM:s = s.encode().decode('utf-8-sig');删末尾逗号:import re; s = re.sub(r',\s*([}\]])', r'\1', s);替换单引号为双引号(仅当无嵌套单引号时谨慎用:s.replace("'", '"')) -
用
ast.literal_eval()替代(限Python字面量):如果源数据本质是Python dict/list字面量(如{'a': 1}),且你信任数据来源,ast.literal_eval(s)能解析单引号、注释(需先删)、尾逗号。但它不支持null/true/false,遇到就抛ValueError -
引入
json5或demjson3第三方库:安装pip install json5后,用json5.loads(s)可原生支持注释、单引号、尾逗号、Infinity等;demjson3更激进,甚至容忍部分语法错误。但要注意:它们不是JSON标准实现,跨语言互通时可能出问题
try-except只该捕获并处理,不该掩盖问题根源
写try: json.loads(s) except JSONDecodeError as e:本身没错,但别只打印错误然后跳过。至少做三件事:
- 记录原始字符串的前100字符和错误位置(
e.pos) - 检查是否来自不可信输入(如用户提交、爬虫抓取),考虑加白名单过滤
- 对已知格式缺陷(如固定带BOM的API响应),在
except块里调用预清洗逻辑重试,而非盲目fallback到ast.literal_eval()
最易被忽略的是:错误位置e.pos指向的是**解码后字符串的偏移**,不是原始字节流位置——如果输入是bytes且含BOM,要先decode再计算,否则日志里的行号会错位。