导读:本期聚焦于半糖创作的《Python 如何让 json.loads() 容忍尾随逗号(非严格 JSON)?》,敬请观看详情。严格模式的 json.loads 遇到 [1,2,] 或 {a:1,} 这类尾随逗号会直接抛出 JSONDecodeError,而实际工作中从日志、配置文件、前端传参里拿到的数据经常带着这种非标准写法,解析失败让人头疼。本文围绕几种可行方案展开:先用 try/except 加预处理正则去掉多余逗号的轻量做法,再介绍 demjson3、json5、dirty-json 等第三方库的处理能力与局限,最后讲解 json.decoder 中 strict 参数的真实作用边界,以及如何用 decoupler 思路封装一个稳健的宽松解析函数。文中给出完整可运行的代码示例,对比各方案的容错范围和性能差异,帮你选对场景下的最优解。

标准 JSON 规范明确禁止尾随逗号,所以当你用 json.loads('[1, 2, 3,]') 解析一段带有尾随逗号的数据时,Python 会毫不留情地抛出 json.decoder.JSONDecodeError: Expecting valueExpecting ',' delimiter。但现实往往是残酷的:日志系统导出的数组、第三方接口拼出来的字符串、甚至同事手写的配置文件,都可能出现 {"name": "test",} 这种写法。浏览器端的 JavaScript 早就容忍了这种写法,可 Python 标准库的态度非常坚决。这篇文章就来聊聊怎么优雅地让 Python 容忍尾随逗号。

Python 如何让 json.loads() 容忍尾随逗号(非严格 JSON)?

先弄清楚错误是怎么发生的

在动手解决问题之前,先看看尾随逗号到底在哪个环节出问题。下面的代码模拟了最常见的两种情况:数组尾部多了一个逗号,以及字典最后一个键值对后面多了一个逗号。

import json

# 数组尾随逗号
try:
    json.loads('[1, 2, 3,]')
except json.JSONDecodeError as e:
    print("数组错误:", e.msg, "位置:", e.pos)

# 对象尾随逗号
try:
    json.loads('{"name": "test",}')
except json.JSONDecodeError as e:
    print("对象错误:", e.msg, "位置:", e.pos)

运行后你会看到,数组的情况报的是 Expecting value,因为解析器在读完逗号后期待下一个元素,结果碰到了右中括号。对象的情况则报 Expecting property name enclosed in double quotes,原因类似——解析器期待下一个键名,却等来了右花括号。理解这一点很重要,因为它决定了后面的修复方案必须在语法层面做处理,而不是简单粗暴地删字符。

顺便澄清一个常见误解:json.loadsstrict 参数跟尾随逗号没有任何关系。它的作用是控制是否允许字符串内部出现控制字符(比如换行符、制表符),默认 strict=True 时字符串里不能包含裸露的控制字符。传 strict=False 并不能解决尾随逗号问题,网上不少文章在这里含糊其辞,容易误导人。

方案一:正则预处理,动手删掉多余的逗号

最直接的思路是先把字符串里的尾随逗号删掉,再交给 json.loads 处理。关键在于正则要写得足够准确:既要匹配 ,] 也要匹配 ,},而且必须考虑到逗号和括号之间可能存在空白字符、换行符。

import json
import re

# 匹配逗号后面(可有空白)紧跟 ] 或 } 的情况
_TRAILING_COMMA = re.compile(r',(\s*[}\]])')

def tolerant_loads(text):
    cleaned = _TRAILING_COMMA.sub(r'\1', text)
    return json.loads(cleaned)

data = tolerant_loads('''
{
    "users": [
        {"id": 1, "name": "张三",},
        {"id": 2, "name": "李四",},
    ],
    "total": 2,
}
''')
print(data)

这个方案最大的优点是不引入任何外部依赖,代码只有几行,对于偶尔需要处理脏数据的脚本来说非常合适。由于 json.loads 本身是用 C 实现的(_json 模块),预处理之后的解析速度依然很快。

但它也有一个不容忽视的隐患:正则无法理解 JSON 的语义。如果字符串值内部恰好包含了 ,] 这样的字面内容,比如 {"desc": "价格区间[10,20,]"},这条正则就会误伤,把字符串里面的内容也改掉,轻则数据被污染,重则解析结果直接错误。要规避这个问题,一个稳妥的做法是先尝试直接解析,失败了才做正则清洗:

def safe_loads(text):
    try:
        return json.loads(text)  # 标准JSON直接走快速路径
    except json.JSONDecodeError:
        cleaned = _TRAILING_COMMA.sub(r'\1', text)
        return json.loads(cleaned)  # 清洗后再试一次

这样标准数据完全不受正则影响,只有真正解析失败时才启用清洗逻辑,风险被压缩到最小。

方案二:借助第三方宽松解析库

如果脏数据的来源不可控,除了尾随逗号还可能有不带引号的键名、单引号字符串、注释等五花八门的问题,正则方案就力不从心了,这时候可以考虑专门的宽松解析库。

第一个推荐的是 json5,它实现了 JSON5 规范,天然支持尾随逗号、单引号、注释、十六进制数字等扩展语法,解析结果的 API 和标准 json 模块几乎一致,学习成本极低:

# pip install json5
import json5

data = json5.loads('''
{
    // 支持注释
    name: '张三',        // 键名可以不带引号
    tags: ['a', 'b', 'c',],
}
''')
print(data)

第二个选择是 demjson3,它是老牌宽松解析库 demjson 的维护分支,容错能力很强,甚至能修复一些缺失引号、多余逗号混用的问题。它的 decode 方法还提供参数控制是否严格报错。不过 demjson3 是纯 Python 实现,数据量大时性能明显不如标准库,只适合处理配置文件这类小体量数据。

# pip install demjson3
import demjson3

# 非严格模式,容忍各种小毛病
data = demjson3.decode('{"name": "test",}')
print(data)

还有 dirty-json 这个库,专门为解析“脏”JSON 设计,对 JavaScript 对象字面量的兼容性不错。三个库的取舍大致是:数据来源接近 JSON5 规范就用 json5;数据来源是 JavaScript 代码片段、格式比较随意就用 demjson3dirty-json;追求性能且问题仅限于尾随逗号,就用前面的正则方案。

方案三:从源头解决,别让脏数据产生

坦白说,前面两种方案都属于“下游兜底”,治标不治本。尾随逗号之所以存在,通常是因为数据生产方用了字符串拼接而不是序列化工具。如果生产方也是你的代码,最好的办法是让对方统一用 json.dumps 生成数据,从根上保证格式合法。

如果数据源无法控制(比如第三方系统、历史日志),建议在接收层统一封装一个入口函数,所有外部 JSON 都经过它进来,内部实现 try/except 加降级逻辑,并记录解析失败的原始数据用于排查。一个工程化的写法如下:

import json
import re
import logging

logger = logging.getLogger(__name__)

def robust_json_loads(text, default=None):
    if not isinstance(text, str):
        return default
    try:
        return json.loads(text)
    except json.JSONDecodeError as e:
        logger.warning("JSON解析失败,尝试宽松清洗: %s, 原文: %.200s", e, text)
        try:
            cleaned = re.sub(r',(\s*[}\]])', r'\1', text)
            return json.loads(cleaned)
        except json.JSONDecodeError:
            logger.error("清洗后仍无法解析,返回默认值")
            return default

这个函数有三个层次的保护:类型检查挡住非字符串输入,快速路径处理标准 JSON,清洗降级处理尾随逗号,最后还有默认值兜底。配合日志记录,即使出现意料之外的格式问题,也能快速定位到原始数据。

总结一下选型建议:临时脚本用正则清洗就够了;应用层长期处理外部脏数据就封装统一的入口函数;数据格式复杂多变就直接上 json5。切记别指望 strict=False 能解决问题,那是字符串控制字符的开关,跟尾随逗号半点关系都没有。

json.loads尾随逗号Python JSON解析修改时间:2026-09-05 10:56:39

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/20260905/50863.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。