如果你曾经在解析 JSON 时看到 SyntaxError: Unexpected token,你并不孤单。这是 JavaScript、Python、Go 及所有其他语言中最常见的 JSON 错误。好消息是:JSON 语法很简单,修复方法几乎总是以下十种常见错误之一。本指南展示了每种错误模式、准确的修复方法,以及防止再次发生的最佳调试工具。
1. "Unexpected Token" 在 JSON 中意味着什么
当 JSON 解析器遇到违反 JSON 规范的字符时,它会抛出一个错误,包含位置和有问题的字符。不同运行环境的错误消息略有不同:
JavaScript(浏览器/Node.js):
SyntaxError: Unexpected token ' in JSON at position 1Python:
json.decoder.JSONDecodeError: Expecting property name enclosed in double quotes: line 1 column 2 (char 1)Go:
invalid character '\'' looking for beginning of valueJava (Jackson):
com.fasterxml.jackson.core.JsonParseException: Unexpected character ('\'' (code 39)): was expecting double-quote to start field name所有这些消息都意味着同一件事:你的 JSON 不是合法的。解析器在该位置发现了一个根据 JSON 规范(RFC 8259)不允许出现的字符。
JSON 比 JavaScript 对象字面量更严格。它要求键和字符串使用双引号,不允许尾随逗号,不支持注释,且只接受有限的值类型:字符串、数字、布尔值(true/false)、null、对象和数组。
2. 十大 JSON 语法错误(附修复前后对比)
错误 1:尾随逗号
JSON 不允许在对象或数组的最后一项之后有逗号。JavaScript 允许,这就是为什么这个错误如此常见。
错误写法:
{
"name": "Alice",
"age": 30,
}错误信息:Unexpected token } in JSON at position 28
正确写法:
{
"name": "Alice",
"age": 30
}错误 2:使用单引号代替双引号
JSON 要求所有字符串和键使用双引号(")。单引号(')不是合法的 JSON。
错误写法:
{'name': 'Alice', 'age': 30}错误信息:Unexpected token ' in JSON at position 1
正确写法:
{"name": "Alice", "age": 30}错误 3:未加引号的键
在 JavaScript 中,对象键不需要引号。在 JSON 中,每个键都必须是双引号字符串。
错误写法:
{name: "Alice", age: 30}错误信息:Expected property name or '}' in JSON at position 2
正确写法:
{"name": "Alice", "age": 30}错误 4:JSON 中的注释
JSON 规范不允许注释。不支持 //、/* */ 或 #。如果需要注释,请使用 JSONC 或 JSON5(见第 6 节)。
错误写法:
{
// This is a comment
"name": "Alice",
/* multi-line
comment */
"age": 30
}错误信息:Unexpected token / in JSON at position 2
正确写法(删除注释):
{
"name": "Alice",
"age": 30
}错误 5:undefined、NaN、Infinity
JSON 只支持 null 作为特殊值。undefined、NaN 和 Infinity 是 JavaScript 特有的,不是合法的 JSON。
错误写法:
{
"value": undefined,
"score": NaN,
"limit": Infinity
}错误信息:Unexpected token u in JSON at position 12
正确写法:
{
"value": null,
"score": 0,
"limit": 9999999
}错误 6:多余或缺失的逗号
项目之间缺少逗号或有双逗号都会导致解析错误。
错误写法(缺少逗号):
{
"name": "Alice"
"age": 30
}错误写法(双逗号):
{
"name": "Alice",,
"age": 30
}错误信息:Expected ',' or '}' in JSON at position 18
正确写法:
{
"name": "Alice",
"age": 30
}错误 7:括号类型错误(混淆 {} 和 [])
在需要花括号的地方使用了方括号(或反之)是常见的复制粘贴错误。
错误写法:
["name": "Alice", "age": 30]错误信息:Expected property name or '}' in JSON at position 1
正确写法:
{"name": "Alice", "age": 30}错误 8:BOM(字节顺序标记)字符
某些文本编辑器(特别是 Windows 上的)会在文件开头添加不可见的 UTF-8 BOM 字符(\uFEFF)。这个不可见字符会导致 JSON 解析失败。
错误示例(开头有不可见的 BOM):
\uFEFF{"name": "Alice"} ← invisible BOM character before {错误信息:Unexpected token \uFEFF in JSON at position 0
修复方法(解析前去除 BOM):
// JavaScript: strip BOM before parsing
const clean = jsonString.replace(/^\uFEFF/, '');
const data = JSON.parse(clean);
// Node.js: read file and strip BOM
const fs = require('fs');
const raw = fs.readFileSync('data.json', 'utf8');
const data = JSON.parse(raw.replace(/^\uFEFF/, ''));错误 9:引号转义不正确
JSON 字符串内的双引号必须用反斜杠转义。字符串值中未转义的双引号会中断解析器。
错误写法:
{"message": "She said "hello" to me"}错误信息:Unexpected token s in JSON at position 18
正确写法:
{"message": "She said \"hello\" to me"}错误 10:特殊字符和控制字符
换行符、制表符和其他控制字符必须在 JSON 字符串中进行转义。字符串值中的原始换行符不是合法的。
错误写法(字符串中有原始换行符):
{"message": "line one
line two"}错误信息:Unexpected token \n in JSON at position 22
正确写法(使用 \n 转义):
{"message": "line one\nline two"}3. 如何调试 JSON 解析错误
当你遇到 JSON 解析错误时,以下工具和技巧将帮助你找到确切的问题。
浏览器控制台(Chrome/Firefox/Edge)
将你的 JSON 粘贴到浏览器控制台中以获取确切的错误位置:
// Paste this in your browser console:
try {
JSON.parse('{"name": "Alice",}');
} catch (e) {
console.error(e.message);
// → "Expected double-quoted property name in JSON at position 17"
}提示:Chrome DevTools 显示确切的位置编号。从开头数字符以找到有问题的字符。
Node.js
使用 try/catch 代码块获取详细的错误信息:
const fs = require('fs');
try {
const raw = fs.readFileSync('config.json', 'utf8');
const data = JSON.parse(raw);
} catch (err) {
console.error('JSON parse failed:', err.message);
// Shows: "Unexpected token , in JSON at position 42"
// Extract the position and show surrounding context
const pos = err.message.match(/position (\d+)/);
if (pos) {
const p = parseInt(pos[1]);
const raw = fs.readFileSync('config.json', 'utf8');
console.error('Context:', raw.substring(p - 20, p + 20));
console.error(' ' + ' '.repeat(20) + '^');
}
}Python(json.loads)
Python 提供最有用的错误消息,包括行号和列号:
import json
raw = """{"name": "Alice", "age": 30,}"""
try:
data = json.loads(raw)
except json.JSONDecodeError as e:
print(f"Error: {e.msg}")
print(f"Line: {e.lineno}, Column: {e.colno}")
print(f"Character position: {e.pos}")
# Output:
# Error: Expecting property name enclosed in double quotes
# Line: 1, Column: 29
# Character position: 28jq(命令行工具)
jq 是一个强大的命令行 JSON 处理器。将无效 JSON 传递给它,它会准确告诉你问题在哪里:
# Validate a JSON file
$ jq . config.json
# If valid: pretty-printed output
# If invalid: parse error at line X, column Y
# Validate inline JSON
$ echo '{"name": "Alice",}' | jq .
# parse error (Invalid numeric literal) at line 1, column 19提示:如果你的 JSON 在变量中或来自 API,可以直接通过管道传递给 jq:
# Validate JSON from an API response
$ curl -s https://api.example.com/data | jq .
# Validate JSON from a shell variable
$ echo "$MY_JSON_VAR" | jq .4. JSON 验证器:在错误进入生产环境之前捕获它们
预防胜于调试。在你的工作流中使用这些验证器。
在线验证器
粘贴你的 JSON,立即获得带有错误高亮的反馈:
- DevToolBox JSON Formatter — 我们的内置工具可以实时验证、格式化和高亮 JSON 错误。
- JSONLint.com — 经典的在线 JSON 验证器。
- JSON Editor Online — 带有错误高亮的树形视图。
VS Code 内置验证
VS Code 自动验证 .json 文件并用红色波浪线显示错误。无需安装扩展。对于使用带注释的 JSON 的文件(如 tsconfig.json),VS Code 会自动使用 JSONC 解析器。
提示:使用"格式化文档"命令(Shift+Alt+F)自动格式化 JSON。如果格式化失败,说明你的 JSON 有语法错误。
命令行验证器
在终端或 CI 管道中验证 JSON:
# jq — validate and pretty-print
$ jq . data.json
# Python built-in
$ python -m json.tool data.json
# Node.js one-liner
$ node -e "JSON.parse(require('fs').readFileSync('data.json','utf8'))"
# Validate multiple files in a directory
$ for f in *.json; do echo -n "$f: "; jq . "$f" > /dev/null 2>&1 && echo "OK" || echo "INVALID"; donejq . — 验证并美化打印。退出码 0 表示有效。
python -m json.tool — Python 内置,无需安装。
node -e — 使用 Node.js 快速验证。
5. 修复来自 API 的 JSON:处理边界情况
API 有时会返回不是合法 JSON 的数据。以下是常见的场景和修复方法。
API 返回 HTML 而不是 JSON
如果服务器返回 HTML 错误页面(404、500)而不是 JSON,JSON.parse() 会在遇到第一个 < 字符时失败。
修复方法:在解析之前检查 Content-Type 头和响应状态:
async function fetchJSON(url) {
const response = await fetch(url);
// Check status first
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
// Check Content-Type header
const contentType = response.headers.get('content-type');
if (!contentType || !contentType.includes('application/json')) {
const text = await response.text();
throw new Error(
`Expected JSON but got ${contentType}: ${text.substring(0, 100)}...`
);
}
return response.json();
}JSONP 包装的响应
一些旧式 API 将 JSON 包装在函数调用中(callback({...}))。在解析前去除包装:
// JSONP response: callback({"name": "Alice", "age": 30})
function parseJSONP(jsonpString) {
// Strip the function wrapper
const match = jsonpString.match(/^[a-zA-Z_][a-zA-Z0-9_]*\((.*)\);?$/s);
if (match) {
return JSON.parse(match[1]);
}
// If no wrapper found, try parsing as regular JSON
return JSON.parse(jsonpString);
}拼接的 JSON / NDJSON
一些 API 流式传输多个以换行符分隔的 JSON 对象(NDJSON / JSON Lines)。需要逐行解析:
// NDJSON: one JSON object per line
const ndjson = `{"id":1,"name":"Alice"}
{"id":2,"name":"Bob"}
{"id":3,"name":"Charlie"}`;
const objects = ndjson
.split('\n')
.filter(line => line.trim())
.map(line => JSON.parse(line));
console.log(objects);
// [{id:1,name:"Alice"}, {id:2,name:"Bob"}, {id:3,name:"Charlie"}]被截断的 JSON 响应
网络超时或响应大小限制可能会在传输途中截断 JSON,导致无效的 JSON。在解析之前始终进行验证:
function safeJSONParse(text) {
try {
return { data: JSON.parse(text), error: null };
} catch (err) {
return {
data: null,
error: {
message: err.message,
position: err.message.match(/position (\d+)/)?.[1],
preview: text.substring(0, 200) + (text.length > 200 ? '...' : ''),
}
};
}
}
// Usage
const result = safeJSONParse(apiResponse);
if (result.error) {
console.error('Invalid JSON:', result.error.message);
console.error('First 200 chars:', result.error.preview);
}6. JSON5 和 JSONC:何时使用宽松格式
有时严格的 JSON 对配置文件来说太痛苦了。有两种流行的替代方案:
JSONC(带注释的 JSON)
JSONC 允许在 JSON 中使用 // 和 /* */ 注释。它被 VS Code 设置、tsconfig.json 和其他开发者工具使用。
JSONC 示例:
{
// Database configuration
"host": "localhost",
"port": 5432,
/* Credentials — loaded from env in production */
"username": "dev_user",
"password": "dev_pass",
// Enable query logging in development
"logging": true
}在 Node.js 中解析 JSONC:
// Using the 'jsonc-parser' package (used by VS Code internally)
const { parse } = require('jsonc-parser');
const jsoncString = fs.readFileSync('config.jsonc', 'utf8');
const config = parse(jsoncString);
// Or strip comments manually with a regex (simplified, not production-ready)
function stripJsonComments(str) {
return str
.replace(/\/\/.*$/gm, '') // Remove single-line comments
.replace(/\/\*[\s\S]*?\*\//g, ''); // Remove multi-line comments
}JSON5
JSON5 是 JSON 的超集,允许:单引号、不带引号的键、尾随逗号、注释、多行字符串、十六进制数字等。
JSON5 示例:
{
// JSON5 allows all of this:
unquotedKey: 'single quotes are fine',
trailingComma: true,
hexValue: 0xFF,
leadingDot: .5,
multiline: 'line one \
line two',
infinity: Infinity,
nan: NaN,
}在 Node.js 中解析 JSON5:
// npm install json5
const JSON5 = require('json5');
const config = JSON5.parse(fs.readFileSync('config.json5', 'utf8'));
// JSON5 can also stringify
const str = JSON5.stringify(config, null, 2);何时使用各种格式
| 格式 | 适用场景 | 不适用场景 |
|---|---|---|
| JSON | API 通信、数据交换、任何机器对机器格式 | 需要人工频繁编辑的配置文件 |
| JSONC | 配置文件(tsconfig.json、VS Code 设置) | API 响应或数据交换 |
| JSON5 | 以开发者体验为优先的人工编写配置文件 | API 响应或需要标准 JSON 解析器的场景 |
7. 常见问题
为什么 JSON.parse() 对有效的 JavaScript 对象解析失败?
因为 JSON 不是 JavaScript。JSON 要求双引号的键和字符串,不允许尾随逗号,不支持注释、undefined、函数或任何 JavaScript 特有的语法。使用 JSON.stringify(obj) 将 JavaScript 对象转换为有效的 JSON。
如何找到 JSON 错误的准确位置?
大多数解析器在错误消息中包含字符位置(例如"at position 42")。在 VS Code 中,使用 Ctrl+G 跳转到行号,然后在该行上数字符。像 JSONLint 这样的在线验证器会直观地高亮显示确切的错误位置。Python 的 json 模块同时提供行号和列号。
可以在 JSON 中使用单引号吗?
不可以。JSON 规范(RFC 8259)要求所有字符串和属性名使用双引号。单引号不是合法的 JSON。如果你需要单引号支持,可以使用 JSON5,它同时允许单引号和双引号。
为什么我的 JSON 文件在 VS Code 中可以用,但在应用中失败?
VS Code 对许多文件类型使用 JSONC 解析器(带注释的 JSON),如 tsconfig.json 和 .vscode/settings.json。你的应用程序可能使用的是严格的 JSON 解析器。删除所有注释和尾随逗号使其兼容严格 JSON,或者在你的应用中使用 JSONC/JSON5 解析器。
如何处理带有 BOM 字符的 JSON?
在解析之前去除 BOM。在 JavaScript 中:jsonString.replace(/^\uFEFF/, '')。在 Node.js 中,使用 UTF-8 编码读取文件并去除 BOM:fs.readFileSync(path, 'utf8').replace(/^\uFEFF/, '')。大多数现代编辑器都可以配置为保存不带 BOM 的文件。