Python解析非标准JSON的踩坑记录和解决方案
前言
JSON是Web开发中最常用的数据交换格式,但现实世界从来不按规范来。你可能遇到过这些情况:
- 单引号代替双引号:
{'name': 'Tom'} - 末尾多了逗号:
{"name": "Tom",} - 键名没加引号:
{name: "Tom"} - 注释混在里面:
{"name": "Tom" // this is a comment}
Python自带的json.loads()遇到这些会直接报错。本文整理了几种常见的非标准JSON场景,以及对应的解决方案。
场景一:单引号 + 末尾逗号
这是最常见的"脏数据",通常来自手写配置或某些API的日志。
import json
data = "{'name': 'Tom', 'age': 18,}" # 单引号 + 末尾逗号
# json.loads(data) # 报错:JSONDecodeError
解决方案1:用ast.literal_eval(推荐)
import ast
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:先替换再解析
import json
import re
def fix_json(s):
# 单引号 → 双引号(注意避免替换字符串内部的引号)
s = re.sub(r"(?<!\\)'", '"', s)
# 去除末尾逗号
s = re.sub(r',\s*([\]}])', r'\1', s)
return json.loads(s)
data = "{'name': 'Tom', 'age': 18,}"
result = fix_json(data)
print(result)
这种方式更灵活,但正则替换有边界风险,复杂场景容易漏。
场景二:键名未加引号
data = '{name: "Tom", age: 18}' # 键名没引号,不是合法JSON
解决方案:用demjson3库
pip install demjson3
import demjson3
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 /* 成年了 */
}
解决方案:先清注释,再解析
import json
import re
def strip_comments(s):
# 去除 // 注释
s = re.sub(r'//.*', '', s)
# 去除 /* */ 注释
s = re.sub(r'/\*.*?\*/', '', s, flags=re.DOTALL)
return s
raw = '''
{
"name": "Tom", // 用户名
"age": 18
}
'''
clean = strip_comments(raw)
result = json.loads(clean)
print(result)
或者直接用demjson3一步到位:
import demjson3 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 | 正常解析 |
import demjson3
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
知识扩展
在 Python 中解析非标准 JSON 数据,常见的情况包括:
- 键名没有引号(
{name: "value"}) - 字符串使用单引号(
{'key': 'value'}) - 存在注释(
// comment或/* comment */) - 尾随逗号(
[1, 2, 3,]) - 十六进制数字(
0xFF)
处理这类数据不能直接用标准库 json,需要借助第三方库或手动预处理。
1.推荐方案:使用 json5 库(最标准、最全面)
json5 是 JSON5 格式的官方 Python 实现,JSON5 是 JSON 的超集,支持上述所有非标准特性。
pip install json5
import json5
data_str = """
{
// 这是注释
name: 'Alice', // 键名无引号,字符串单引号
age: 30,
tags: ['dev', 'test',], // 尾随逗号
}
"""
obj = json5.loads(data_str)
print(obj) # {'name': 'Alice', 'age': 30, 'tags': ['dev', 'test']}也可以从文件读取:
with open('data.json5', 'r', encoding='utf-8') as f:
obj = json5.load(f)2.备选方案:demjson(老牌,但维护较少)
demjson 也能处理许多非标准情况,但更新不活跃,且依赖较多。
pip install demjson
import demjson
data = "{name: 'Bob', age: 25,}"
obj = demjson.decode(data)
print(obj)3.手动预处理(适合简单情况)
如果只有单引号问题,可以用正则或字符串替换:
import json
data = "{'name': 'John', 'age': 30}" # 单引号
# 简单替换:将外层的单引号替换为双引号
# 注意:仅适用于字符串值不包含引号的情况,否则有风险
data_clean = data.replace("'", '"')
obj = json.loads(data_clean)
print(obj)缺点:若字符串内容本身就含有单引号或双引号,会破坏结构。
4.使用 ast.literal_eval()(仅限 Python 字面量)
如果数据格式严格符合 Python 字面量(单引号、无键名引号等),可以用 ast.literal_eval,但不能解析 true、false、null(Python 是 True、False、None)。
import ast
data = "{name: 'Alice', age: 30}"
obj = ast.literal_eval(data) # 返回 dict
print(obj) # {'name': 'Alice', 'age': 30}局限性:布尔值和 null 会报错,需要手动替换。
总结推荐
- 首选
json5:功能最全、语法标准、维护良好。 - 简单单引号替换:仅适合临时一次性脚本,且数据格式可控。
- 避免使用
demjson:长期未更新,可能不兼容新版 Python。
下面是使用 json5 的完整示例:
import json5
# 示例非标准 JSON
non_standard = """
{
// 这是一条注释
"name": "Mike",
"age": 28,
"address": {
city: 'New York',
zip: 10001,
},
}
"""
parsed = json5.loads(non_standard)
print(parsed['address']['city']) # New York如果你需要处理大量类似数据,建议先统一转换成标准 JSON 格式保存,再使用原生 json 解析提高性能。
总结
非标准JSON的本质是:数据生产方和消费方对"什么是合法JSON"的定义不一致。
Python生态的解决思路也很清晰:
- 轻量场景 →
ast.literal_eval - 重度场景 →
demjson3 - 极致场景 → 自己写清洗逻辑
选哪个,取决于你的数据有多"脏"。
到此这篇关于Python解析非标准JSON的踩坑记录和解决方案的文章就介绍到这了,更多相关Python解析非标准JSON内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
相关文章
一文掌握Python GUI开发利器PySide2的实战指南
PySide2 是由 Qt 公司官方推出的 Python 绑定库,为开发跨平台,现代化 GUI 提供了强大而灵活的支持,下面就跟随小编一起来了解下PySide2的具体使用吧2025-07-07
如何用python复制粘贴excel指定单元格(可保留格式)
这篇文章主要给大家介绍了关于如何用python复制粘贴excel指定单元格(可保留格式)的相关资料,利用python操作excel非常方便,文中通过实例代码介绍的非常详细,需要的朋友可以参考下2023-07-07
python GUI库图形界面开发之PyQt5拖放控件实例详解
这篇文章主要介绍了python GUI库图形界面开发之PyQt5使用拖放控件实例详解,需要的朋友可以参考下2020-02-02


最新评论