Python如何解析非标准JSON:那些坑和解决方案
前言
JSON是Web开发中最常用的数据交换格式,但现实世界从来不按规范来。你可能遇到过这些情况:
- 单引号代替双引号:
{'name': 'Tom'} - 末尾多了逗号:
{"name": "Tom",} - 键名没加引号:
{name: "Tom"} - 注释混在里面:
{"name": "Tom" // this is a comment}
Python自带的json.loads()遇到这些会直接报错。本文整理了几种常见的非标准JSON场景,以及对应的解决方案。
场景一:单引号 + 末尾逗号
这是最常见的"脏数据",通常来自手写配置或某些API的日志。
importjson data="{'name': 'Tom', 'age': 18,}"# 单引号 + 末尾逗号# json.loads(data) # 报错:JSONDecodeError解决方案1:用ast.literal_eval(推荐)
importast data="{'name': 'Tom', 'age': 18,}"result=ast.literal_eval(data)print(result)# {'name': 'Tom', 'age': 18}ast.literal_eval可以安全地解析Python字面量,包括单引号字符串、末尾逗号、True/False/None等。
⚠️ 注意:它只能解析Python语法能识别的值,不能处理真正的JSON扩展。
解决方案2:先替换再解析
importjsonimportredeffix_json(s):# 单引号 → 双引号(注意避免替换字符串内部的引号)s=re.sub(r"(?<!\\)'",'"',s)# 去除末尾逗号s=re.sub(r',\s*([\]}])',r'\1',s)returnjson.loads(s)data="{'name': 'Tom', 'age': 18,}"result=fix_json(data)print(result)这种方式更灵活,但正则替换有边界风险,复杂场景容易漏。
场景二:键名未加引号
data='{name: "Tom", age: 18}'# 键名没引号,不是合法JSON解决方案:用demjson3库
pipinstalldemjson3importdemjson3 data='{name: "Tom", age: 18}'result=demjson3.decode(data)print(result)# {'name': 'Tom', 'age': 18}demjson3是目前处理非标准JSON最强的第三方库,支持:
- 无引号键名
- 单引号
- 末尾逗号
- 注释(
//和/* */) - JavaScript风格的
undefined、NaN、Infinity
场景三:包含注释的JSON
某些配置文件会在JSON里加注释,这在标准JSON中是不允许的。
{"name":"Tom",// 用户名"age":18/* 成年了 */}解决方案:先清注释,再解析
importjsonimportredefstrip_comments(s):# 去除 // 注释s=re.sub(r'//.*','',s)# 去除 /* */ 注释s=re.sub(r'/\*.*?\*/','',s,flags=re.DOTALL)returns raw=''' { "name": "Tom", // 用户名 "age": 18 } '''clean=strip_comments(raw)result=json.loads(clean)print(result)或者直接用demjson3一步到位:
importdemjson3 result=demjson3.decode(raw)场景四:JavaScript风格的特殊值
{"value":undefined,"number":NaN,"big":Infinity}这些在标准JSON中不存在,但在JS生态中很常见。
| JS值 | JSON等价 | demjson3处理结果 |
|---|---|---|
undefined | 无 | None |
NaN | 无 | nan(float) |
Infinity | 无 | inf(float) |
true/false | true/false | 正常解析 |
importdemjson3 data='{value: undefined, num: NaN, big: Infinity}'result=demjson3.decode(data)print(result)# {'value': None, 'num': nan, 'big': inf}方案对比
| 方案 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
json.loads | 标准JSON | 快、安全、内置 | 只能处理标准JSON |
ast.literal_eval | Python风格字典 | 安全、内置 | 不识别null/true/false |
demjson3 | 几乎所有非标准情况 | 功能最全 | 性能较慢,依赖第三方 |
| 正则替换+json | 简单单引号/逗号场景 | 轻量 | 容易出错,维护成本高 |
实践建议
- 能用标准JSON就用标准JSON——这是根治问题的方式
- 数据来源可信 + 格式简单→ 用
ast.literal_eval,性能好且安全 - 数据来源杂、格式乱→ 直接上
demjson3,别自己造轮子 - 生产环境对性能敏感→ 先做数据清洗,再用
json.loads
总结
非标准JSON的本质是:数据生产方和消费方对"什么是合法JSON"的定义不一致。
Python生态的解决思路也很清晰:
- 轻量场景 →
ast.literal_eval - 重度场景 →
demjson3 - 极致场景 → 自己写清洗逻辑
选哪个,取决于你的数据有多"脏"。
