วิธีแก้ JSON parse error: ตรวจ Unexpected token ทีละขั้น
สรุปสำคัญ: แปลง error ของ JSON.parse ให้เป็นบรรทัดและตัวอักษร แล้วตัดสาเหตุหกประการออกไป: เครื่องหมายจุลภาคท้ายรายการ เครื่องหมายคำพูดเดี่ยว คอมเมนต์ อักขระควบคุม BOM และการตัดทอนข้อมูล

สรุปสั้น: เมื่อเกิด parse error ให้อ่านบรรทัดและคอลัมน์ที่รายงานเพื่อหาอักขระผิดกฎตัวแรก แล้วตัดสินว่าเป็นเครื่องหมายคำพูดที่ขาด เครื่องหมายจุลภาคที่ขาด เครื่องหมายจุลภาคท้ายรายการ BOM หรือการตัดทอนข้อมูล — อย่าอ่านเอกสารใหม่ทั้งหมดตั้งแต่ต้น
ข้อความ error ไม่เคยบอกว่า "อะไรผิด" มันบอกแค่ว่า "ตัวแยกวิเคราะห์ไปต่อไม่ได้ที่ไหน" สองตำแหน่งนี้มักไม่ใช่ที่เดียวกัน
1. แปลง error เป็นพิกัดก่อน
JSON ที่พังแบบเดียวกันให้ error ต่างกันโดยสิ้นเชิงในแต่ละภาษา แต่ทั้งหมดแปลงเป็น "บรรทัดไหน ตัวอักษรตัวไหน" ได้:
| สภาพแวดล้อม | error ที่พบบ่อย | วิธีอ่าน |
|---|---|---|
| เบราว์เซอร์ / Node | Unexpected token } in JSON at position 42 |
position คือ character offset เริ่มนับที่ 0 ไม่ใช่เลขบรรทัด |
| Node 20+ | Unexpected non-whitespace character after JSON at position 42 |
หมายถึงส่วนแรกถูกต้อง และเนื้อหาเกินเริ่มหลังตำแหน่ง 42 |
| Java Jackson | Unexpected character ('}' (code 125)): was expecting ... at [Source: (String)..."..." ; line: 3, column: 1] |
ให้บรรทัดและคอลัมน์โดยตรง ใช้งานได้ดีที่สุด |
| Python | json.decoder.JSONDecodeError: Expecting ',' delimiter: line 3 column 1 (char 42) |
char คือ offset; บรรทัดและคอลัมน์คือสิ่งที่สำคัญ |
| Go | invalid character '}' looking for beginning of object key string |
ให้เฉพาะอักขระ ต้องย้อนกลับไปหาตำแหน่งเอง |
เมื่อได้ตำแหน่งแล้ว ให้วาง JSON ลงใน ตัวแยกวิเคราะห์ JSON ออนไลน์; เครื่องมือจะวางเคอร์เซอร์ที่ตำแหน่ง error และไฮไลต์เลขบรรทัดและคอลัมน์ — ขั้นตอนนี้ประหยัดเวลาไล่ดูอักขระได้ 90%
2. หก error ที่พบบ่อย
1. เครื่องหมายจุลภาคท้ายรายการ
{
"name": "json",
"version": 1,
}
มีเครื่องหมายจุลภาคเกินมาก่อน } ข้อกำหนด JSON ไม่อนุญาตเครื่องหมายจุลภาคท้ายรายการ ขณะที่ object literal ของ JavaScript อนุญาต คอนฟิกที่คัดลอกจากโค้ดจึงมีโอกาสพามาด้วยมากที่สุด วิธีแก้: ลบเครื่องหมายจุลภาคตัวสุดท้าย; ถ้าข้อมูลนี้อยู่ในไฟล์ JS ตั้งแต่แรก ให้เปลี่ยนไปใช้ การแปลง JSON5 ดีกว่าฝืนให้เป็น JSON
2. เครื่องหมายคำพูดเดี่ยวและคีย์ที่ไม่ครอบเครื่องหมายคำพูด
{ 'name': 'json' }
{ name: "json" }
สตริง JSON ใช้ได้เฉพาะเครื่องหมายคำพูดคู่ และคีย์ก็ต้องเป็นสตริงที่ครอบเครื่องหมายคำพูดคู่เช่นกัน สองแบบนี้เป็นไวยากรณ์แบบผ่อนปรนของ JSON5 / JavaScript ไม่ใช่ JSON เครื่องหมายคำพูดคู่ภายในสตริงต้องเขียนเป็น \" และแบ็กสแลชเป็น \\
3. คอมเมนต์
{
// Timeout in milliseconds
"timeout": 3000
}
ไวยากรณ์ของ RFC 8259 ไม่มีที่ให้คอมเมนต์ ให้ลบคอมเมนต์ออก หรือบันทึกเป็น .jsonc / .json5 แล้วใช้ตัวแยกวิเคราะห์ที่รองรับคอมเมนต์ สำหรับข้อแลกเปลี่ยนของ "วิธีใส่คอมเมนต์" แบบต่าง ๆ ดู วิธีเขียนคอมเมนต์ใน JSON
4. การขึ้นบรรทัดใหม่และอักขระควบคุมดิบภายในสตริง
{"sql": "SELECT *
FROM t"}
สตริง JSON บรรจุ line feed, แท็บ หรืออักขระควบคุม U+0000–U+001F แบบดิบไม่ได้ ต้องเขียนเป็น \n, \t, \u0009 เมื่อต้องยัดข้อความหลายบรรทัดลงใน JSON ให้ escape ในเอดิเตอร์ก่อนครั้งหนึ่ง หรือจัดการอย่างสม่ำเสมอด้วย บีบอัดและ escape JSON
5. ส่วนหัว BOM
UTF-8 ที่บันทึกโดย Notepad ของ Windows พาไบต์สามตัว EF BB BF มาด้วย JSON.parse รายงาน Unexpected token เมื่อเจอ BOM และสายตามองไม่เห็นเลย วิธีตรวจ: ดูส่วนหัวไฟล์ในรูป hex หรือคัดลอกเนื้อหาไปยังเครื่องมือออนไลน์แล้วดูว่าอักขระแรกแสดงเป็น หรือไม่ วิธีแก้: บันทึกเป็น "UTF-8 without BOM"
6. เนื้อหาถูกตัดทอน
log ที่พิมพ์เพียง 4096 ตัวอักษรแรก response ของ API ที่ถูก gateway ตัด การดาวน์โหลดที่ถูกขัดจังหวะ — ลายเฉพาะของกลุ่มนี้คือ "error อยู่ที่ท้าย" และจำนวนวงเล็บไม่ตรงกัน ให้นับวงเล็บก่อน: ถ้าวงเล็บปิดของ { และ [ ไม่ตรงกัน แสดงว่าถูกตัดทอน ไม่ใช่ syntax error ในกรณีนั้นอย่าแก้ไขอะไร — ไปเอาข้อมูลฉบับเต็มมา
3. สองการเตือนที่หลอกเรื่องข้อความจีนและการ escape
ข้อความจีนกลายเป็น \u4e2d\u6587: นี่คือการ escape ของ JSON ที่ถูกต้อง ไม่ใช่ตัวขยะ หลังแยกวิเคราะห์ ค่าจะกลับมาเป็นภาษาจีน; รูปแบบ \u จะแสดงก็ต่อเมื่อคุณดูสตริงดิบโดยตรง เมื่อต้องแสดงให้คนเห็น ก็แค่ส่งออกด้วย เครื่องมือจัดรูปแบบ; คุณต้องสนใจกลยุทธ์การ escape ก็ต่อเมื่อภาษาจีนต้องอยู่รอดใน URL หรือ log
ข้อความจีนกลายเป็นตัวขยะอย่าง 涓枃: นี่คือปัญหาการเข้ารหัส ไม่ใช่ปัญหาของ JSON เกิดเมื่อไบต์ UTF-8 ถูกถอดรหัสเป็น GBK ได้ผลลัพธ์เป็นรูปแบบ "สามตัวอักษรแทนหนึ่งอักขระจีน" นี้ ตรวจส่วนหัวของ response ว่าเป็น Content-Type: application/json; charset=utf-8 และตรวจว่าได้ระบุ encoding='utf-8' อย่างชัดเจนตอนอ่านไฟล์หรือไม่
4. ขั้นตอนการซ่อมภายในห้านาที
- วางลงในเครื่องมือ: ใช้ ตัวแยกวิเคราะห์ออนไลน์ หา error ตัวแรกก่อน และแก้ทีละจุดเท่านั้น;
- ตรวจสอบหลังแก้ทุกครั้ง: แยกวิเคราะห์ใหม่ทุกครั้งที่เปลี่ยน เพื่อไม่ให้การแก้หลายจุดพร้อมกันทำให้ error ใหม่กลบ error เก่า;
- บีบอัดหนึ่งครั้ง: ตัดช่องว่างด้วย Minify แล้วแยกวิเคราะห์ใหม่; ขั้นนี้แยก "error เชิงโครงสร้าง" ออกจาก "ปัญหาช่องว่างหรือการขึ้นบรรทัดใหม่";
- เปรียบเทียบโครงสร้าง: ถ้า error โผล่ขึ้นมาหลังการเปลี่ยนแปลง ให้ใช้ JSON diff เทียบกับเวอร์ชันก่อนหน้า; ความต่างมักเป็นเครื่องหมายจุลภาคตัวเดียว;
- ตรวจระดับฟิลด์: ถ้าไวยากรณ์ผ่านแต่ชั้นธุรกิจปฏิเสธ (ฟิลด์หาย ชนิดผิด) นั่นเป็นเรื่องของ JSON Schema validation ไม่ใช่ปัญหาทางไวยากรณ์
5. ทำไม JSON สองชิ้นที่ "หน้าตาเหมือนกัน" แยกวิเคราะห์ผ่านชิ้นเดียว
สาเหตุที่พบมากที่สุดคือ อักขระที่มองไม่เห็น: เครื่องหมายจุลภาคแบบเต็มความกว้าง , เครื่องหมายคำพูดแบบเต็มความกว้าง “” ช่องว่างแบบไม่ตัดบรรทัด U+00A0 และช่องว่างความกว้างศูนย์ U+200B บนหน้าจอแทบแยกไม่ออกจากตัวครึ่งความกว้าง แต่ไม่มีตัวใดเป็นส่วนหนึ่งของไวยากรณ์ JSON
มีวิธีเดียวที่จะรู้: คัดลอกเนื้อหารอบ ๆ อักขระที่มีปัญหาแล้วดู code point ทีละไบต์ การรันต่อไปนี้ในคอนโซลเบราว์เซอร์จะแสดงอักขระทุกตัวที่ไม่ใช่ ASCII และอยู่นอกช่วงภาษาจีน:
[...text].filter((ch) => {
const code = ch.codePointAt(0);
return code > 127 && !(code >= 0x4e00 && code <= 0x9fff);
});
ถ้า 0x201c (เครื่องหมายคำพูดคู่ซ้าย), 0xff0c (เครื่องหมายจุลภาคเต็มความกว้าง) หรือ 0x00a0 โผล่ในเอาต์พุต แสดงว่าตัวนั้นคือต้นเหตุ
6. การป้องกันถูกกว่าการซ่อม
- รันการตรวจ parse กับไฟล์คอนฟิกก่อนคอมมิต บรรทัดเดียวใน CI ก็พอ:
node -e "JSON.parse(require('fs').readFileSync('config.json','utf8'))"; - สร้าง response ของ API ด้วยไลบรารีทำให้เป็นอนุกรม ห้ามต่อสตริงด้วยมือ — สตริงที่ประกอบด้วยมือเป็นต้นเหตุเดียวของเครื่องหมายจุลภาคท้ายรายการและเครื่องหมายคำพูดที่ขาด;
- เมื่อบันทึก log ของ JSON ให้เรียก
JSON.stringifyก่อน อย่าแทรกเข้าไปในเทมเพลต เพื่อไม่ให้ payload ที่เขียนค้างครึ่งทางถูกเข้าใจผิดว่าเป็นข้อความที่สมบูรณ์
คำถามที่พบบ่อย
จะหาตำแหน่งของ JSON syntax error ได้เร็วอย่างไร
ใช้ตำแหน่งหรือบรรทัด/คอลัมน์ที่ตัวแยกวิเคราะห์รายงาน; position คือ character offset เริ่มนับที่ 0 ไม่ใช่เลขบรรทัด ขณะที่ Java Jackson และ Python รายงานบรรทัดและคอลัมน์โดยตรง วาง JSON ลงใน ตัวแยกวิเคราะห์ JSON ออนไลน์ แล้วเครื่องมือจะวางเคอร์เซอร์ที่ตำแหน่ง error และไฮไลต์บรรทัดและคอลัมน์ ถ้า error อยู่ที่จุดเริ่มต้น ให้ตรวจส่วนหัวไฟล์ว่ามี BOM และมองหาอักขระที่มองไม่เห็น
ทำไมคอนฟิกที่คัดลอกจากโค้ดจึงมีโอกาสพาเครื่องหมายจุลภาคท้ายรายการมาด้วย
ข้อกำหนด JSON ห้ามเครื่องหมายจุลภาคท้ายรายการ ขณะที่ object literal ของ JavaScript อนุญาต คอนฟิกที่คัดลอกมาจากโค้ด JS จึงพามาได้ง่าย วิธีแก้ก็แค่ลบเครื่องหมายจุลภาคตัวสุดท้าย; ถ้าข้อมูลอยู่ในไฟล์ JS อยู่แล้ว การเปลี่ยนไปใช้ การแปลง JSON5 เข้ากว่าการฝืนให้เป็น JSON
แยกวิเคราะห์ไฟล์คอนฟิกที่มีคอมเมนต์เป็น JSON ปกติได้ไหม
ไม่ได้ — ไวยากรณ์ของ RFC 8259 ไม่มีที่ให้คอมเมนต์ การแยกวิเคราะห์โดยตรงจึงเกิด error ให้ลบคอมเมนต์ออก หรือบันทึกเป็น .jsonc / .json5 แล้วใช้ตัวแยกวิเคราะห์ที่รองรับคอมเมนต์ สำหรับข้อแลกเปลี่ยนของวิธีใส่คอมเมนต์ ดู วิธีเขียนคอมเมนต์ใน JSON