裝置與系統問題
為什麼 JSON.parse 會說「Unexpected token」?
當文字不是有效 JSON 時,JSON.parse 會丟出 Unexpected token。請排查單引號、尾隨逗號、HTML 回應與錯誤位置。
當輸入不是有效 JSON 時,JSON.parse() 會丟出 SyntaxError。「Unexpected token」一般代表剖析器碰到了當下結構不允許的字元。
常見成因包含單引號、尾隨逗號、未加引號的成員名稱、不正確的字串跳脫、JSON 前後多餘文字,或把 HTML 等非 JSON 本文當成 JSON 來剖析。
精確的錯誤措辭會隨 JavaScript 引擎與瀏覽器而不同。切勿認定每個執行環境都會印出 “Unexpected token X in JSON at position N”。
這篇專講 JSON.parse 的 Unexpected token 排查:代表什麼、怎麼讀 position、本文是 HTML 時該怎麼辦。尾隨逗號、引號與 Unexpected end 的更廣說明在 常見 JSON 錯誤:尾隨逗號、引號與 Unexpected token。
JSON.parse() 出現「Unexpected token」代表什麼?
JSON.parse() 要求符合 JSON 文法。目前位置若出現非法字元就會丟出 SyntaxError。{“name”:“Alex”,} 無效:逗號之後物件不能結束。訊息文字與 position 仍會因執行環境而異。
JSON.parse() 會丟出哪一種錯誤?
無效 JSON 多半會丟出 SyntaxError。把 error.name 記到日誌以確認類型。不要把完整的 error.message 寫死:引擎與版本的用詞不同。
try {
JSON.parse('{"name":"Alex",}');
} catch (error) {
console.log(error.name);
// SyntaxError
}
1. 用了單引號而不是雙引號
{‘name’:‘Alex’} 不是有效 JSON。{“name”:“Alex”} 才有效。JSON 的字串與物件成員名稱使用雙引號。像 const user = { name: ‘Alex’ } 這種 JavaScript 物件常值可以是合法 JavaScript,卻仍不是 JSON 文字。
無效
{'name':'Alex'}
合法
{"name":"Alex"}
2. 尾隨逗號
最後一個屬性或陣列項目後面的逗號,在標準 JSON 裡無效。{“name”:“Alex”,“age”:30,} 與 [1, 2, 3,] 都會失敗。把它視為 Unexpected token 的成因之一;常見 JSON 錯誤:尾隨逗號、引號與 Unexpected token 對尾隨逗號有更深入的說明。
無效
{
"name": "Alex",
"age": 30,
}
合法
{
"name": "Alex",
"age": 30
}
[1, 2, 3,]
3. 未加引號的成員名稱
{ name: “Alex” } 可以是 JavaScript 物件常值。在 JSON 裡,物件成員名稱是字串,必須加上雙引號:{“name”:“Alex”}。
無效
{
name: "Alex"
}
合法
{
"name": "Alex"
}
4. 不合法的字串跳脫
在 JSON 文字裡,反斜線會開啟一段跳脫。C:\Users\Alex 這種原始路徑無效,因為 \U 不是合法跳脫。請把反斜線寫成兩道。不要把宿主語言的字串跳脫和 JSON 跳脫混在一起。更深入的說明見 常見 JSON 錯誤:尾隨逗號、引號與 Unexpected token。
無效
{"file":"C:\Users\Alex"}
合法
{"file":"C:\\Users\\Alex"}
5. JSON 前面或後面多出來的文字
JSON.parse 期望恰好一個 JSON 值。在 {“ok”:true} 前面加上 Success:,或在後面加上 done,會讓整段字串無效。單一個 JSON 值周圍無意義的空白字元是允許的。JSON 並未禁止值的前面或後面出現空格。
Success:
{"ok":true}
{"ok":true}
done
6. 把 HTML 當成 JSON 來剖析
對 HTML 頁面呼叫 response.json() 是常見的 Unexpected token 情況。錯誤端點、404 或 500 的 HTML、登入重新導向、代理本文、後援頁,開頭往往是標記。「Unexpected token < 一定就是 HTML」說得太滿:< 只是線索,不是證明。
fetch('/api/data')
.then(response => response.json())
「Unexpected token <」通常代表什麼?
JSON 值通常不以 < 開頭。若本文以 或 起頭,< 可能是第一個非法字元。本機排查時請看 HTTP 狀態、Content-Type 與原始本文。引擎用詞仍然會變。
剖析之前先檢查回應
fetch 的本文只能取用一次。本機排查時先讀 text(),檢查一小段前綴,再對同一字串做 JSON.parse。不要對同一份回應先呼叫 response.json() 再呼叫 response.text()。預設不要把正式環境的敏感內容寫進日誌。
const response = await fetch(url);
const text = await response.text();
console.log(response.status);
console.log(response.headers.get("content-type"));
console.log(text.slice(0, 200));
try {
const data = JSON.parse(text);
console.log(data);
} catch (error) {
console.error("Invalid JSON:", error);
}
「at Position 123」代表什麼?
有些引擎會回報 position、列號或欄號。到附近找壞掉的引號、逗號、括號或跳脫。那個索引是剖析器察覺問題的位置;真正原因可能更前面。它並不保證就是那個出錯字元。
Unexpected token 對上 Unexpected end
Unexpected token 表示剖析器碰到了它沒預期的字元。輸入在值寫完之前就停住時,可能出現 Unexpected end,例如 {“name”:“Alex” 少了右花括號。確切用詞取決於引擎。常見 JSON 錯誤:尾隨逗號、引號與 Unexpected token 對不完整 JSON 有更廣的說明。
{"name":"Alex"
JSON 並不是 JavaScript 物件常值
JSON 是資料格式。JavaScript 物件常值是 JavaScript 語法。帶未加引號鍵、單引號與尾隨逗號的 JS 物件不是有效 JSON 文字。JSON.parse 無法安全地剖析 JavaScript 物件常值。
const data = {
name: 'Alex',
active: true,
};
{
"name": "Alex",
"active": true
}
JSON 裡能寫註解嗎?
標準 JSON 不支援註解。物件裡的 // 列是無效的。JSON5 及類似擴充是另一種格式。JSON 格式化、驗證與比對 以 JSON.parse 驗證標準 JSON,不會把 JSON5 當成 JSON。
{
// user name
"name": "Alex"
}
undefined、NaN 與 Infinity 是有效 JSON 嗎?
不是。JSON 的值只有 object、array、string、number、true、false 與 null。undefined、NaN 與 Infinity 都不算。頂層不必是物件或陣列:true、123、“hello” 與 null 都是有效 JSON。
{"a":undefined}
{"a":NaN}
{"a":Infinity}
true
123
"hello"
null
如何逐步修正 Unexpected token
動手改來源,而不是改錯誤字串:
- 確認輸入確實是 JSON,而不是 HTML、紀錄或 JavaScript。
- 檢查原始輸入開頭幾個字元。
- 若來自 API,檢查 HTTP 狀態與 Content-Type。200 與 application/json 都不能保證本文是有效 JSON。
- 用 JSON 剖析器驗證語法。
- 把回報的 position、列號或欄號當線索,不要當成鐵定的根本原因。
- 檢查那一帶的引號、逗號、花括號、方括號與跳脫。
- 對照一份已排版、已驗證的副本。
- 去修序列化器或端點。不要隨機刪掉被回報的記號。
把 ' 全域換成 " 會弄壞字串裡的撇號、已經跳脫的文字,以及非 JSON 來源。要從真正的序列化器產出有效 JSON。不要把 eval 當成 JSON 剖析器。
在 JavaScript 產出 JSON 時請使用 JSON.stringify()
優先使用 JSON.stringify,而不是用手把字串接起來。它對能序列化的值會放出有效 JSON 文字。它不會原樣保留每一種 JavaScript 值:undefined、函式與符號有特殊行為。細節超出本篇範圍。
const data = {
name: "Alex",
active: true
};
const json = JSON.stringify(data);
// {"name":"Alex","active":true}
無效 JSON 對照真正的修法
下面這些對照已用 JSON.parse 核對過。遇到 HTML 時,該修的是回應,不是去改語法。
| 狀況 | 無效寫法 | 修法 |
|---|---|---|
| 單引號 | {'a':1} |
{"a":1} |
| 尾隨逗號 | {"a":1,} |
{"a":1} |
| 未加引號的鍵 | {a:1} |
{"a":1} |
| 出現 undefined | {"a":undefined} |
{"a":null} |
| HTML 本文 | <html>... |
檢查端點與原始回應。不要把 HTML 改寫成 JSON。 |
一份簡單的 Unexpected token 決策指南
JSON.parse 失敗之後,先判斷手上到底有沒有 JSON,再去改引號。
- JSON.parse 沒有成功
- 來自 API?查看狀態、Content-Type 與原始本文
- 是否以 JSON 開頭?若不是,去修來源或端點
- 若是,驗證引號、逗號、花括號、方括號、跳脫、不受支援的值
- 把回報的 position 當線索
- 修好來源,再剖析一次
剖析或使用之前先驗證 JSON
NEXNARA JSON 格式化、驗證與比對 在瀏覽器裡用 JSON.parse 驗證 JSON——不是 eval。
可驗證,用 2 空白、4 空白或定位字元美化,精簡,複製,下載,上傳 .json。JSON Diff 涵蓋巢狀物件與陣列、路徑、摘要、篩選與對調。剖析之後,同一份資料的美化版與精簡版比較為相等;物件鍵依名稱比較。
大於 5 MB 的文字可能變慢。大於 15 MB 的文字不會在瀏覽器裡處理。
JSON 驗證與排版在你的瀏覽器裡完成。此工具不會把 JSON 送到 NEXNARA 伺服器。廣告與其他站台功能仍會使用網路。
把失敗的字串貼進 JSON 格式化、驗證與比對,先看開頭幾個字元,再去改引號。
FAQ
為什麼 JSON.parse 會說 Unexpected token?
剖析器停下的那一點,文字不是有效 JSON。典型成因是引號、逗號、多餘文字,或 HTML 這類非 JSON 本文。
JSON 裡的 Unexpected token < 是什麼意思?
第一個非法字元可能是 <。這常常指向 HTML 頁面,但不能證明每次都是 404 或都是 HTML。請查看狀態、Content-Type 與原始本文。
怎樣找出 JSON 裡的錯誤位置?
若引擎回報了 position、列號或欄號,到附近查看。真正的錯誤可能更前面。把它當線索,然後再驗證。
JSON 能用單引號嗎?
不能。標準 JSON 的字串與成員名稱使用雙引號。
JSON 能有尾隨逗號嗎?
不能。標準 JSON 不允許最後一個屬性或陣列項目後面再跟逗號。
怎樣檢查一段字串是不是有效 JSON?
在 try/catch 裡呼叫 JSON.parse,或把文字貼進 JSON 格式化、驗證與比對。若剖析丟出 SyntaxError,該字串就不是有效 JSON。