چگونه خطاهای تجزیه JSON را رفع کنیم: بررسی گامبهگام Unexpected token
جمعبندی: خطاهای JSON.parse را به شماره خط و کاراکتر تبدیل کنید و سپس شش علت را کنار بگذارید: ویرگول انتهایی، تکگیومه، کامنت، کاراکتر کنترلی، BOM و بریدگی.

جمعبندی: هنگام خطای تجزیه، خط و ستون گزارششده را بخوانید تا نخستین کاراکتر غیرمجاز را پیدا کنید، سپس تصمیم بگیرید که آیا گیومه جا افتاده، ویرگول گم شده، ویرگول انتهایی هست، BOM هست یا متن بریده شده — کل سند را از ابتدا دوباره نخوانید.
پیام خطا هرگز نمیگوید «چه چیزی اشتباه است»؛ فقط میگوید «تجزیهکننده دیگر از کجا نتوانست پیش برود». این دو موقعیت اغلب یک جا نیستند.
۱. نخست خطا را به مختصات تبدیل کنید
همان JSON شکسته در زبانهای مختلف خطاهای کاملاً متفاوتی میدهد، اما همه آنها را میتوان به «کدام خط، کدام کاراکتر» تبدیل کرد:
| محیط | خطای معمول | چگونه بخوانیم |
|---|---|---|
| مرورگر / Node | Unexpected token } in JSON at position 42 |
position یک افست کاراکتری از صفر است، نه شماره خط |
| Node 20+ | Unexpected non-whitespace character after JSON at position 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 افست است؛ خط و ستون همان چیزی است که اهمیت دارد |
| Go | invalid character '}' looking for beginning of object key string |
فقط کاراکتر را میدهد، پس باید خودتان عقب بروید و آن را پیدا کنید |
وقتی موقعیت را به دست آوردید، JSON را در تجزیهکننده آنلاین JSON بچسبانید؛ ابزار مکاننما را روی محل خطا میگذارد و شماره خط و ستون را برجسته میکند — همین مرحله ۹۰٪ چشمدواندن میان کاراکترها را صرفهجویی میکند.
۲. شش خطای پرتکرار
۱. ویرگول انتهایی
{
"name": "json",
"version": 1,
}
پیش از } یک ویرگول اضافه وجود دارد. مشخصات JSON ویرگول انتهایی را مجاز نمیداند، حال آنکه لیترالهای object در JavaScript آن را میپذیرند، بنابراین پیکربندی کپیشده از کد بیش از همه ممکن است یکی را حمل کند. رفع: آخرین ویرگول را حذف کنید؛ اگر این داده از ابتدا در یک فایل JS زندگی میکند، بهجای زور زدن برای تبدیل به JSON، از تبدیل JSON5 استفاده کنید.
۲. تکگیومه و کلید بدون گیومه
{ 'name': 'json' }
{ name: "json" }
رشتههای JSON فقط میتوانند از گیومه دوتایی استفاده کنند و کلیدها هم باید رشته درون گیومه دوتایی باشند. این دو، نحو تسهیلیافته JSON5 / JavaScript هستند، نه JSON. گیومه دوتایی درون رشته باید بهصورت \" و بکاسلش بهصورت \\ نوشته شود.
۳. کامنتها
{
// Timeout in milliseconds
"timeout": 3000
}
دستور زبان RFC 8259 جایی برای کامنت ندارد. یا کامنتها را حذف کنید یا فایل را با پسوند .jsonc / .json5 ذخیره کنید و از تجزیهکنندهای که کامنت را پشتیبانی میکند استفاده کنید. برای مقایسه گزینههای مختلف «طرح کامنت»، چگونه در JSON کامنت بنویسیم را ببینید.
۴. خط جدید خام و کاراکتر کنترلی درون رشته
{"sql": "SELECT *
FROM t"}
رشتههای JSON نمیتوانند خط جدید خام، تب یا کاراکترهای کنترلی U+0000–U+001F را در خود داشته باشند؛ باید بهصورت \n، \t، \u0009 نوشته شوند. هنگام چپاندن متن چندخطی در JSON، نخست آن را در یک ویرایشگر escape کنید یا بهطور یکسان با فشردهسازی و escape JSON مدیریت کنید.
۵. هدر BOM
UTF-8 ذخیرهشده با Notepad ویندوز سه بایت EF BB BF را حمل میکند. JSON.parse هنگام روبهرو شدن با BOM خطای Unexpected token میدهد و چشم اصلاً آن را نمیبیند. راه تشخیص: هدر فایل را بهصورت هگز بررسی کنید، یا محتوا را در یک ابزار آنلاین بچسبانید و ببینید آیا کاراکتر اول بهصورت نمایش داده میشود. رفع: با «UTF-8 without BOM» ذخیره کنید.
۶. محتوای بریده
گزارشی که فقط ۴۰۹۶ کاراکتر اول را چاپ کرده، پاسخ API که gateway آن را بریده، دانلودی که نیمهکاره مانده — نشانه این دسته «خطا در انتها است» و تعداد براکتها نمیخواند. نخست براکتها را بشمارید: اگر بستهشدن { و [ با هم نمیخواند، بریدگی است نه خطای نحوی. در این صورت چیزی را اصلاح نکنید — بروید داده کامل را بیاورید.
۳. دو هشدار کاذب درباره متن چینی و escape
متن چینی به \u4e2d\u6587 تبدیل میشود: این escape معتبر JSON است، نه آشوب. پس از تجزیه، مقدار به چینی بازمیگردد؛ شکل \u فقط وقتی دیده میشود که رشته خام را مستقیم نگاه کنید. وقتی باید به افراد نشان دهید، کافی است با ابزار قالببندی خروجی بگیرید؛ فقط وقتی باید به استراتژی escape اهمیت دهید که متن چینی باید در URL یا گزارشی زنده بماند.
متن چینی به آشوبی مانند 涓枃 تبدیل میشود: این مسئله رمزگذاری است، نه JSON. وقتی بایتهای UTF-8 بهصورت GBK رمزگشایی شوند رخ میدهد و این الگوی منظم «سه کاراکتر بهجای یک کاراکتر چینی» را میسازد. هدر پاسخ Content-Type: application/json; charset=utf-8 را بررسی کنید و ببینید هنگام خواندن فایل encoding='utf-8' صریح مشخص شده است یا نه.
۴. جریان ترمیم پنجدقیقهای
۱. در ابزار بچسبانید: نخست با تجزیهکننده آنلاین نخستین خطا را پیدا کنید و هر بار فقط یکی را اصلاح کنید؛ ۲. بعد از هر اصلاح اعتبارسنجی کنید: پس از هر تغییر دوباره تجزیه کنید تا اصلاح چند جا با هم، خطاهای تازه را روی خطای قبلی پنهان نکند؛ ۳. یک بار فشرده کنید: با فشردهسازی فاصلهها را حذف کنید و دوباره تجزیه کنید؛ این کار «خطاهای ساختاری» را از «مشکل فاصله یا خط جدید» جدا میکند؛ ۴. ساختار را مقایسه کنید: اگر خطایی ناگهان پس از یک تغییر ظاهر شد، با تفاوت JSON آن را با نسخه قبلی بسنجید؛ تفاوت معمولاً همان یک ویرگول است؛ ۵. اعتبارسنجی در سطح فیلد: اگر نحو عبور کرد اما لایه کسبوکار رد کرد (فیلد غایب، نوع نادرست)، این زیر اعتبارسنجی JSON Schema قرار میگیرد، نه مسئله نحو.
۵. چرا از دو قطعه 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 در خروجی ظاهر شود، همان مقصر است.
۶. پیشگیری ارزانتر از ترمیم است
- پیش از commit، فایلهای پیکربندی را با تجزیه بررسی کنید؛ یک خط در CI کافی است:
node -e "JSON.parse(require('fs').readFileSync('config.json','utf8'))"؛ - پاسخهای API را با کتابخانه سریالسازی تولید کنید؛ هرگز رشتهها را دستی به هم نچسبانید — رشتههای دستساز تنها منبع ویرگول انتهایی و گیومه جاافتادهاند؛
- هنگام ثبت JSON در گزارش، نخست
JSON.stringifyرا صدا بزنید نه اینکه آن را در قالب درج کنید، تا payload نیمهنوشته با پیام کامل اشتباه نشود.
پرسشهای متداول
چگونه سریع موقعیت خطای نحوی JSON را پیدا کنم؟
موقعیت یا خط/ستون گزارششده توسط تجزیهکننده را بگیرید؛ position یک افست کاراکتری از صفر است، نه شماره خط، در حالی که Java Jackson و Python خط و ستون را مستقیم گزارش میکنند. JSON را در تجزیهکننده آنلاین JSON بچسبانید و ابزار مکاننما را روی محل خطا میگذارد و خط و ستون را برجسته میکند. اگر خطا در ابتدا باشد، هدر فایل را برای BOM و کاراکترهای نامرئی بررسی کنید.
چرا پیکربندی کپیشده از کد اینقدر احتمال دارد ویرگول انتهایی داشته باشد؟
مشخصات JSON ویرگول انتهایی را ممنوع میکند در حالی که لیترالهای object در JavaScript آن را مجاز میدانند، پس پیکربندی کپیشده از کد JS بهآسانی یکی را با خود میآورد. رفع ساده است: آخرین ویرگول را حذف کنید؛ اگر داده بههرحال به یک فایل JS تعلق دارد، رفتن به تبدیل JSON5 مناسبتر از زور زدن برای JSON است.
آیا میتوانم فایل پیکربندی حاوی کامنت را بهعنوان JSON معمول تجزیه کنم؟
نه — دستور زبان RFC 8259 جایی برای کامنت ندارد، پس تجزیه مستقیم خطا میدهد. یا کامنتها را حذف کنید یا فایل را با .jsonc / .json5 ذخیره کنید و از تجزیهکنندهای که کامنت را پشتیبانی میکند استفاده کنید. برای مقایسه طرحهای کامنت، چگونه در JSON کامنت بنویسیم را ببینید.