JSONDecodeError 常因文件内容不合规触发,如多余逗号、单引号、注释、BOM头或非UTF-8字符;应使用python -m json.tool验证、指定encoding='utf-8-sig'、用with安全读取,并考虑json5等兼容库处理类JSON文本。

JSONDecodeError 常见触发原因和快速定位方法
报错 JSONDecodeError: Expecting value 或类似信息,基本说明 Python 的 json.loads() 或 json.load() 读到了非合法 JSON 内容。不是代码写错了,而是文件本身不合规——比如多了逗号、用了单引号、包含注释、BOM 头、或混入了非 UTF-8 字符。
先用命令行快速验证:python -m json.tool your_file.json。如果报错,错误位置会直接标出;没报错但 Python 仍失败,大概率是编码问题。
- Windows 记事本保存的 JSON 文件常带
\ufeff(UTF-8 BOM),json.load()会把它当非法字符 - 前端调试时手写的 JSON 常用单引号
'key': 'value',但 JSON 规范只认双引号 - 配置类 JSON 文件里加了
// 注释或/* ... */,JSON 标准不支持注释
用 open() 读取时必须指定 encoding='utf-8-sig'
这是解决 BOM 问题最直接的办法。utf-8-sig 会在解码时自动剥离开头的 \ufeff,而普通 utf-8 会把它当普通字符处理,导致解析失败。
错误写法:json.load(open('config.json')) —— 缺少 encoding,且未用 with 管理文件句柄
立即学习“Python免费学习笔记(深入)”;
正确写法:
with open('config.json', encoding='utf-8-sig') as f:
data = json.load(f)- 永远用
with open(...),避免文件句柄泄露 - 不要依赖系统默认编码(Windows 下常是 cp1252),显式声明
encoding是底线 - 如果文件明确是 GBK 编码(如某些旧日志),改用
encoding='gbk',但需确认内容不含 UTF-8 特有字符
如何安全加载可能含注释或单引号的“类 JSON”文本
真实项目里,很多所谓 “JSON 文件” 其实是开发者手动维护的配置,带注释、单引号、尾随逗号——它们不是 JSON,而是 JSON5 或 TOML 风格。硬用 json 模块必然失败。
SkillSub Pro - Python 题解与代码注释双功能技能功能概述SkillSub Pro - Python 题解与代码注释双功能技能是一项面向实际任务的技能,主要用于SkillSub Pro 是一个 Python 题解生成与代码注释的 双功能合体技能 ,专为学生、算法学习者和开发者设计;✅ 一个技能,两种用途 :;核心要点📝 题解模式 :输入题目/题号,自动生成完整 Python 题解(含详细注释、解题思路、复杂度分析);💬 注释模式 :输入 Python 代码,自动添加详细中。它将相关步骤、
方案选择取决于你能否控制输入源:
- 能改文件格式:转成标准 JSON(删注释、换双引号、去尾逗号),用原生
json - 不能改文件,且只是读配置:用
json5库(pip install json5),它兼容注释、单引号、尾逗号 - 更复杂需求(如支持变量替换):考虑
pyyaml(YAML 语法更宽松,yaml.safe_load()可读类似 JSON 的结构)
示例(json5):
import json5
with open('config.json', encoding='utf-8-sig') as f:
data = json5.load(f) # 自动跳过 // 和 /* */ 注释捕获异常并打印原始出错片段
别只靠 traceback 判断问题。JSON 解析失败时,JSONDecodeError 对象自带位置信息,可以提取出错前后几行,方便人工排查。
推荐这样写:
try:
with open('data.json', encoding='utf-8-sig') as f:
data = json.load(f)
except json.JSONDecodeError as e:
print(f'JSON decode error at line {e.lineno}, col {e.colno}: {e.msg}')
# 打印出错行及前后两行
lines = open('data.json', encoding='utf-8-sig').readlines()
start = max(0, e.lineno - 3)
end = min(len(lines), e.lineno + 2)
for i in range(start, end):
marker = '>>>' if i == e.lineno - 1 else ' '
print(f'{marker} {i+1:3d} | {lines[i].rstrip()}')
注意:这里两次打开文件,仅用于调试;生产环境应缓存文件内容或只打印行号供人工查。
真正难搞的不是语法错,而是看似合法的 JSON 实际含不可见控制字符(比如从 Excel 复制粘贴进来的零宽空格 \u200b),这类问题必须靠肉眼或 hexdump 查看原始字节。

















