JSONDecodeError 定位
json.loads() 报错时,JSONDecodeError 对象携带精确位置信息:
import json
try:
data = json.loads(content)
except json.JSONDecodeError as e:
print(f"错误信息: {e.msg}")
print(f"行号: {e.lineno}, 列号: {e.colno}")
print(f"字符偏移: {e.pos}")
print(f"出错内容: {content[max(0, e.pos-50):e.pos+50]}")
原因一:裸 NaN / Infinity
JSON 规范不允许 NaN 和 Infinity 作为数值字面量,只有加了引号的字符串 "NaN" 才合法:
# 合法
json.loads('{"value": "NaN"}') # OK,返回字符串
# 非法
json.loads('{"value": NaN}') # JSONDecodeError: Expecting value
json.loads('{"value": Infinity}') # JSONDecodeError
如果数据源返回了裸 NaN,需要预处理:
import re
cleaned = re.sub(r'\bNaN\b', '"NaN"', content)
data = json.loads(cleaned)
原因二:内容不是完整 JSON
常见错误是将多个 JSON 对象直接拼接(不用数组包裹):
# 非法:两个对象并排
content = '{"a": 1}, {"b": 2}'
json.loads(content) # JSONDecodeError
# 合法:用数组包裹
content = '[{"a": 1}, {"b": 2}]'
json.loads(content) # OK
另一种情况是只收到了响应的一部分(网络截断),pos 错误位置通常在字符串末尾。
原因三:数字存为字符串
JSON 中数字加了引号,解析后得到字符串而非数值:
{"x": "1228.8650628969294", "z": "NaN"}
data = json.loads(content)
type(data["x"]) # str,不是 float
# 需要手动转换
x = float(data["x"])
# z 是 "NaN" 字符串,转 float 得到 nan
z = float(data["z"]) # float('nan')
import math
math.isnan(z) # True
原因四:编码问题
非 UTF-8 编码的文件用 json.loads 读取会报错:
# 读取文件时指定编码
with open('data.json', 'r', encoding='utf-8') as f:
data = json.load(f)
# 如果来源是 bytes
data = json.loads(raw_bytes.decode('utf-8'))
快速调试模板
import json, traceback
def safe_parse(content: str):
try:
return json.loads(content), None
except json.JSONDecodeError as e:
context_start = max(0, e.pos - 30)
context_end = min(len(content), e.pos + 30)
snippet = content[context_start:context_end]
marker = ' ' * (e.pos - context_start) + '^'
print(f"JSONDecodeError at line {e.lineno}, col {e.colno}:")
print(snippet)
print(marker)
return None, e