你把看起來合法的資料貼到 JSON parser 裡,它立刻丟出 SyntaxError: Unexpected token '''。這段資料來自你的 JavaScript 原始碼,看起來完全合理 —— 但 JSON 與 JavaScript 物件字面量並不是同一種格式,差異比大多數開發者意識到的更重要。
簡短答案:JSON 不支援單引號
在 JSON 中單引號完全非法。每一個字串 —— key 與值都算 —— 都必須使用雙引號。沒有例外。
// ❌ 非法 JSON —— 單引號字串
{ 'name': 'Ada Lovelace', 'active': true }
// ✅ 合法 JSON
{ "name": "Ada Lovelace", "active": true }你在不同環境會看到的錯誤:
// Chrome / Node.js (V8)
SyntaxError: Unexpected token "'", "{'name':..." is not valid JSON
// Firefox
SyntaxError: JSON.parse: unexpected character at line 1 column 2 of the JSON data
// Safari
SyntaxError: JSON Parse error: Single quotes (') are not allowed in JSONSafari 的錯誤訊息最有用 —— 它直接告訴你問題究竟是什麼。其他幾個只是回報那個意外字元。
為什麼 JSON 要求雙引號?
JSON 由 Douglas Crockford 在 2001 年定義,標準化為 RFC 8259。語法明確規定只有一種字串定界符:雙引號字元(U+0022)。單引號根本不在語法中。
原因是簡單。JSON 是一種資料交換格式,目的是被任何語言解析,而不只是 JavaScript。規範強制一種引號風格,消除了一整類歧義。Go、Python、Java、Rust 以及任何其他語言的 parser 都實作了完全相同的規則。
JSON vs JavaScript 物件字面量:完整差異
單引號只是諸多差異之一。完整版如下:
1. key 必須是雙引號字串
// ❌ JavaScript 物件字面量 —— 裸 key、單引號 key
{ name: "Ada", 'score': 98 }
// ✅ JSON —— 所有 key 都是雙引號字串
{ "name": "Ada", "score": 98 }JavaScript 物件字面量接受裸識別字、單引號字串與雙引號字串作為 key。JSON 只接受雙引號字串。
2. 不允許尾隨逗號
// ❌ 非法 JSON
{ "name": "Ada", "score": 98, }
// ^ 尾隨逗號
// ✅ 合法 JSON
{ "name": "Ada", "score": 98 }現代 JavaScript 明確允許尾隨逗號。JSON 不允許 —— 見我們關於 JSON 尾隨逗號的完整指南。
3. 沒有註解
// ❌ 非法 JSON
{
// 使用者個人檔
"name": "Ada"
}
// ✅ JSON 沒有註解語法
{ "name": "Ada" }JSON 根本沒有任何註解語法。若你需要在設定檔中加註解,可考慮 JSONC 格式(JSON-with-Comments),VS Code 設定、TypeScript 的 tsconfig.json 及若干設定檔 parser 都支援。
4. 沒有 undefined、函式或 Symbol
// JavaScript 物件 —— 接受不可序列化的值
const obj = {
fn: () => 42, // 函式
sym: Symbol('id'), // Symbol
val: undefined, // undefined
};
// JSON.stringify 會悄悄丟掉或轉換它們
JSON.stringify(obj);
// → '{}' (這三個屬性都沒了或變成 null) JSON 恰好支援六種值型別:string、number、boolean(true/false)、null、object 與 array。函式、Symbol 與 undefined 在 JSON 中沒有任何表示法。JSON.stringify() 會默默丟掉物件中含這些值的屬性,並把它們在陣列裡轉成 null。
5. 沒有 NaN 或 Infinity
JSON.stringify({ ratio: NaN, limit: Infinity });
// → '{"ratio":null,"limit":null}'
// NaN 與 Infinity 變成 null —— 還是悄悄的!
JSON.parse('{"ratio": NaN}');
// → SyntaxError: Unexpected token 'N'NaN 與 Infinity 是合法的 JavaScript 數字值,但不屬於 JSON 數字規格。JSON.stringify() 出去時把它們轉成 null;JSON.parse() 進來時則拒絕。這種不對稱常導致隱晦的 bug。
6. 數字:沒有十六進位、沒有前導零、沒有 +
// ❌ 非法 JSON
{ "flags": 0xFF, "code": 007, "delta": +1.5 }
// ✅ 合法 JSON
{ "flags": 255, "code": 7, "delta": 1.5 } JSON 數字必須為十進位,不能帶顯式的 + 號,也不能有前導零(除了小數點前那一個 0)。十六進位字面量與八進位字面量都不是合法 JSON。
7. 沒有多行字串
// ❌ 非法 JSON —— 字串內含字面換行
{ "bio": "Line one
Line two" }
// ✅ 使用轉義序列
{ "bio": "Line one\nLine two" }8. 計算屬性名、Symbol 鍵與方法簡寫
另外三個看似 JSON 但在 JSON 中沒有等價的 JS 專屬特性:
// ❌ 計算屬性名 —— 是運算式,不是字面量
const k = "id";
const obj = { [k]: 1, [`user_${k}`]: 1 };
// JSON 沒有運算式;key 必須是字面意義上的雙引號字串。
// ❌ Symbol 鍵 —— 會被 JSON.stringify 默默丟掉
const tag = Symbol('tag');
JSON.stringify({ [tag]: 'admin' }); // → '{}'
// ❌ 方法簡寫 —— 函式會被丟掉/省略
const obj = { greet() { return 'hi'; } };
JSON.stringify(obj); // → '{}' 若你需要其中任何一項,就把物件序列化成 JSON 形狀的投影 —— JSON.stringify() 永遠只會產出字串 key 與那六種 JSON 值型別。
快速轉換 cheatsheet
| JavaScript 物件字面量 | 合法 JSON 等價寫法 |
|---|---|
{ name: "Ada" } | {"name":"Ada"} |
{ 'name': 'Ada' } | {"name":"Ada"} |
{ a: 1, } | {"a":1} |
{ val: undefined } | {}(屬性被丟) |
{ n: NaN } | {"n":null} |
{ n: Infinity } | {"n":null} |
{ fn: () => 1 } | {}(屬性被丟) |
0xFF | 255 |
把 JS 物件轉成 JSON 的最穩妥方法
切勿透過手動編輯引號把 JavaScript 物件轉成 JSON。用 JSON.stringify() —— 它會正確處理每一種邊界情況:
const obj = { name: 'Ada', score: 98, active: true };
// 緊湊
JSON.stringify(obj);
// → '{"name":"Ada","score":98,"active":true}'
// 帶 2 個空白縮排的 pretty-print
JSON.stringify(obj, null, 2);
// → '{
"name": "Ada",
"score": 98,
"active": true
}' JSON.stringify() 的第二個引數是一個 replacer —— 你可以傳一個 key 名陣列只包含那些屬性,或傳一個函式來變換值:
// 只包含指定 key
JSON.stringify(obj, ['name', 'score'], 2);
// → '{
"name": "Ada",
"score": 98
}'
// 自訂 replacer —— 把 undefined 轉成 null
JSON.stringify({ a: 1, b: undefined }, (key, value) =>
value === undefined ? null : value
);
// → '{"a":1,"b":null}'當你拿到一段 JS 物件字面量字串時
有時你拿到的資料看起來像 JSON,其實是 JavaScript 物件字面量 —— 可能來自日誌、除錯工具,或某個未遵守規範的 API。這時 JSON.parse() 會失敗,你有兩個選擇:
- 用一個修復 parser —— 把單引號轉雙引號、去掉尾隨逗號、為裸 key 加引號,自動處理上述所有差異。對不可信輸入而言這是最穩的方法。
- 用
eval()或Function()—— 技術上對合法 JavaScript 可以,但只要來源不完全可信就是嚴重的安全風險。絕對不要對使用者輸入這麼做。
常見問題
JSON 支援單引號嗎?
不支援。JSON 語法(RFC 8259)只定義一種字串定界符 —— 雙引號。key 與值都必須使用雙引號;單引號會丟 SyntaxError。
JSON 與 JavaScript 物件的差異是什麼?
JSON 是嚴格的文字格式;JavaScript 物件字面量是寬鬆的程式碼。JS 允許單引號、不加引號的 key、尾隨逗號、註解、undefined、NaN、函式與十六進位數字 —— 這些都不是合法 JSON。完整 cheatsheet 在上方,入門見 什麼是 JSON?
如何把 JavaScript 物件轉成 JSON?
用 JSON.stringify() —— 絕不要手動改引號。它能處理每一種邊界情況:丟掉不可序列化的值並正確地轉義字串。
為什麼不能用 eval() 解析 JS 物件字面量?
eval() 會執行任意程式碼,對不可信輸入是嚴重的安全風險。改用修復 parser(或 JSON Fix)安全地把 JS 物件字面量轉成合法 JSON。
立刻修好單引號
若你手上有一段 JavaScript 物件字面量、現在就想轉成合法 JSON,JSON Fix 會自動處理。它會把單引號轉雙引號、為裸 key 加引號、去掉尾隨逗號,並修正上面列出的所有其他差異 —— 都在你的瀏覽器中,不會送往任何伺服器。
- JSON Fix —— 把 JS 物件字面量即時轉成合法 JSON
- 修復 JSON 中的單引號 —— 快速參考指南,含壞例與好例
- JSON vs JavaScript 物件字面量 —— 每項語法差異與並排範例
- JSON.parse Unexpected Token 錯誤 —— SyntaxError 每一種變體的指南
- JSON 中的尾隨逗號 —— 為什麼 JS 允許但 JSON 禁止