JSON Toolkit

Cách sửa lỗi phân tích JSON: kiểm tra Unexpected token từng bước

Kết luận: Biến lỗi JSON.parse thành dòng và ký tự, rồi loại trừ sáu nguyên nhân: dấu phẩy cuối, dấu nháy đơn, comment, ký tự điều khiển, BOM và nội dung bị cắt.

Người mớiĐọc khoảng 12 phútXuất bản 2026-10-30Cập nhật 2026-11-01
Ảnh bìa cho chẩn đoán lỗi phân tích JSON

Điểm mấu chốt: khi gặp lỗi phân tích, hãy đọc dòng và cột được báo để tìm ký tự bất hợp lệ đầu tiên, rồi quyết định xem đó là thiếu dấu ngoặc kép, thiếu dấu phẩy, dấu phẩy cuối, BOM hay nội dung bị cắt — đừng đọc lại toàn bộ tài liệu từ đầu.

Thông báo lỗi không bao giờ nói "sai ở đâu"; nó chỉ nói "trình phân tích không thể tiếp tục ở đâu". Hai vị trí đó thường không phải cùng một chỗ.

1. Trước hết, chuyển lỗi thành tọa độ

Cùng một JSON hỏng sẽ tạo ra những lỗi hoàn toàn khác nhau ở các ngôn ngữ khác nhau, nhưng tất cả đều có thể quy về "dòng nào, ký tự nào":

Môi trường Lỗi điển hình Cách đọc
Trình duyệt / Node Unexpected token } in JSON at position 42 position là độ lệch ký tự tính từ 0, không phải số dòng
Node 20+ Unexpected non-whitespace character after JSON at position 42 nghĩa là phần đầu hợp lệ và nội dung thừa bắt đầu sau 42
Java Jackson Unexpected character ('}' (code 125)): was expecting ... at [Source: (String)..."..." ; line: 3, column: 1] cho biết thẳng dòng và cột, dễ dùng nhất trong tất cả
Python json.decoder.JSONDecodeError: Expecting ',' delimiter: line 3 column 1 (char 42) char là độ lệch; dòng và cột mới là thứ quan trọng
Go invalid character '}' looking for beginning of object key string chỉ cho biết ký tự, nên bạn phải tự truy ngược để định vị

Khi đã có vị trí, hãy dán JSON vào trình phân tích JSON trực tuyến; công cụ đặt con trỏ tại vị trí lỗi và tô sáng số dòng, số cột — bước này tiết kiệm 90% thời gian dò ký tự bằng mắt.

2. Sáu lỗi tần suất cao

1. Dấu phẩy cuối

{
  "name": "json",
  "version": 1,
}

Có một dấu phẩy thừa trước }. Đặc tả JSON không cho phép dấu phẩy cuối, trong khi object literal của JavaScript lại cho phép, nên cấu hình sao chép ra từ mã là thứ dễ mang theo dấu phẩy này nhất. Cách sửa: xóa dấu phẩy cuối cùng; nếu dữ liệu này vốn sống trong một tệp JS, hãy chuyển sang chuyển đổi JSON5 thay vì ép nó thành JSON.

2. Dấu nháy đơn và khóa không ngoặc kép

{ 'name': 'json' }
{ name: "json" }

Chuỗi JSON chỉ được dùng dấu ngoặc kép, và khóa cũng phải là chuỗi đặt trong ngoặc kép. Hai ví dụ trên là cú pháp nới lỏng của JSON5 / JavaScript, không phải JSON. Dấu ngoặc kép bên trong chuỗi phải viết là \", và dấu gạch chéo ngược là \\.

3. Comment

{
  // Timeout in milliseconds
  "timeout": 3000
}

Ngữ pháp của RFC 8259 không có chỗ cho comment. Hoặc xóa comment, hoặc lưu dưới dạng .jsonc / .json5 và dùng trình phân tích hỗ trợ comment. Về các đánh đổi giữa những "phương án comment" khác nhau, xem Cách viết comment trong JSON.

4. Xuống dòng trần và ký tự điều khiển bên trong chuỗi

{"sql": "SELECT *
FROM t"}

Chuỗi JSON không được chứa xuống dòng trần, tab, hoặc các ký tự điều khiển U+0000–U+001F; chúng phải được viết là \n, \t, \u0009. Khi nhồi văn bản nhiều dòng vào JSON, hãy escape một lần trong trình soạn thảo trước, hoặc xử lý thống nhất bằng nén và escape JSON.

5. Header BOM

UTF-8 do Notepad trên Windows lưu mang theo ba byte EF BB BF. JSON.parse báo Unexpected token khi gặp BOM, mà mắt thì hoàn toàn không thấy. Cách nhận biết: xem header tệp dưới dạng hex, hoặc sao chép nội dung vào một công cụ trực tuyến và xem ký tự đầu tiên có hiển thị thành  không. Cách sửa: lưu dưới dạng "UTF-8 without BOM".

6. Nội dung bị cắt

Một bản log chỉ in 4096 ký tự đầu, một phản hồi API bị gateway cắt ngang, một lần tải xuống bị gián đoạn — dấu hiệu đặc trưng của nhóm này là "lỗi nằm ở cuối" và số lượng ngoặc không khớp. Hãy đếm ngoặc trước: nếu số ngoặc đóng của { và [ không khớp, đó là bị cắt, không phải lỗi cú pháp. Trường hợp đó đừng sửa gì cả — hãy đi lấy dữ liệu đầy đủ.

3. Hai báo động giả liên quan đến tiếng Trung và escape

Tiếng Trung biến thành \u4e2d\u6587: đây là escape JSON hợp lệ, không phải văn bản lỗi font. Sau khi phân tích, giá trị được khôi phục thành tiếng Trung; dạng \u chỉ hiện ra khi bạn nhìn trực tiếp vào chuỗi thô. Khi cần cho người khác xem, chỉ cần xuất ra bằng công cụ định dạng; bạn chỉ cần quan tâm đến chiến lược escape khi tiếng Trung buộc phải tồn tại trong URL hoặc log.

Tiếng Trung biến thành văn bản lỗi font kiểu 涓枃: đây là vấn đề mã hóa, không phải vấn đề JSON. Nó xảy ra khi các byte UTF-8 bị giải mã như GBK, tạo ra kiểu mẫu đều đặn "ba ký tự thay cho một chữ Hán". Hãy kiểm tra Content-Type: application/json; charset=utf-8 trong header phản hồi, và kiểm tra xem encoding='utf-8' có được chỉ định tường minh khi đọc tệp không.

4. Quy trình sửa chữa năm phút

  1. Dán vào công cụ: trước hết dùng trình phân tích trực tuyến để định vị lỗi đầu tiên, và mỗi lần chỉ sửa một chỗ;
  2. Xác thực sau mỗi lần sửa: phân tích lại sau mỗi thay đổi, để việc sửa nhiều chỗ cùng lúc không khiến lỗi mới che mất lỗi cũ;
  3. Nén một lần: gỡ khoảng trắng bằng Nén rồi phân tích lại; bước này phân biệt "lỗi cấu trúc" với "vấn đề khoảng trắng hoặc xuống dòng";
  4. So sánh cấu trúc: nếu sau một thay đổi mà lỗi đột nhiên xuất hiện, dùng JSON diff so với phiên bản trước; khác biệt thường chỉ là một dấu phẩy;
  5. Xác thực ở mức trường: nếu cú pháp qua nhưng tầng nghiệp vụ từ chối (thiếu trường, sai kiểu), thì đó thuộc về xác thực JSON Schema, không phải vấn đề cú pháp.

5. Vì sao một trong hai đoạn JSON "trông giống hệt" lại phân tích được còn đoạn kia không

Nguyên nhân phổ biến nhất là ký tự vô hình: dấu phẩy full-width ,, dấu ngoặc kép full-width “”, khoảng trắng không ngắt dòng U+00A0, khoảng trắng độ rộng bằng không U+200B. Trên màn hình chúng gần như không thể phân biệt với bản nửa độ rộng, nhưng không ký tự nào trong số đó thuộc ngữ pháp JSON.

Chỉ có một cách để nhận ra: sao chép nội dung quanh ký tự gây lỗi và kiểm tra code point từng byte một. Chạy đoạn sau trong console trình duyệt sẽ liệt kê mọi ký tự không phải ASCII và nằm ngoài các dải tiếng Trung:

[...text].filter((ch) => {
  const code = ch.codePointAt(0);
  return code > 127 && !(code >= 0x4e00 && code <= 0x9fff);
});

Nếu 0x201c (dấu ngoặc kép mở), 0xff0c (dấu phẩy full-width), hoặc 0x00a0 xuất hiện trong đầu ra, thì chúng chính là thủ phạm.

6. Phòng ngừa rẻ hơn sửa chữa

  • Chạy kiểm tra phân tích trên tệp cấu hình trước khi commit; một dòng trong CI là đủ: node -e "JSON.parse(require('fs').readFileSync('config.json','utf8'))";
  • Sinh phản hồi API bằng một thư viện tuần tự hóa; đừng bao giờ nối chuỗi bằng tay — chuỗi tự ghép là nguồn duy nhất của dấu phẩy cuối và dấu ngoặc kép thiếu;
  • Khi ghi log JSON, hãy gọi JSON.stringify trước thay vì nội suy nó vào template, để một payload viết dở không bị nhầm là thông điệp hoàn chỉnh.

Câu hỏi thường gặp

Làm sao định vị nhanh vị trí của một lỗi cú pháp JSON?

Lấy vị trí hoặc dòng/cột mà trình phân tích báo; position là độ lệch ký tự tính từ 0, không phải số dòng, trong khi Java Jackson và Python báo thẳng dòng và cột. Dán JSON vào trình phân tích JSON trực tuyến và công cụ đặt con trỏ tại vị trí lỗi cùng tô sáng dòng, cột. Nếu lỗi nằm ở đầu, hãy kiểm tra header tệp xem có BOM và tìm các ký tự vô hình.

Vì sao cấu hình sao chép từ mã lại dễ mang dấu phẩy cuối?

Đặc tả JSON cấm dấu phẩy cuối trong khi object literal của JavaScript cho phép, nên cấu hình sao chép ra từ mã JS dễ mang theo một dấu. Cách sửa đơn giản là xóa dấu phẩy cuối cùng; nếu dữ liệu vốn thuộc về một tệp JS, chuyển sang chuyển đổi JSON5 phù hợp hơn là ép nó thành JSON.

Tôi có thể phân tích một tệp cấu hình có comment như JSON thông thường không?

Không — ngữ pháp của RFC 8259 không có chỗ cho comment, nên phân tích trực tiếp sẽ báo lỗi. Hoặc xóa comment, hoặc lưu dưới dạng .jsonc / .json5 và dùng trình phân tích hỗ trợ comment. Về các đánh đổi giữa các phương án comment, xem Cách viết comment trong JSON.