前言

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风格的undefinedNaNInfinity

场景三:包含注释的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 简单单引号/逗号场景 轻量 容易出错,维护成本高

实践建议

  1. 能用标准JSON就用标准JSON——这是根治问题的方式
  2. 数据来源可信 + 格式简单 → 用ast.literal_eval,性能好且安全
  3. 数据来源杂、格式乱 → 直接上demjson3,别自己造轮子
  4. 生产环境对性能敏感 → 先做数据清洗,再用json.loads

总结

非标准JSON的本质是:数据生产方和消费方对"什么是合法JSON"的定义不一致

Python生态的解决思路也很清晰:

  • 轻量场景 → ast.literal_eval
  • 重度场景 → demjson3
  • 极致场景 → 自己写清洗逻辑

选哪个,取决于你的数据有多"脏"。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐