در آموزش‌های مقدماتی، JSON همیشه تمیز و مرتب است؛ چند جفت کلید-مقدار ساده که به‌راحتی parse می‌شوند. اما در پروژه واقعی، اولین باری که یک API پاسخ ۴۰۰ کیلوبایتی برگرداند یا یک فایل پیکربندی با کاراکتر عجیب شما را در ترمینال زمین بزند، می‌فهمید داستان جدی‌تر از چند خط مثال است. من در پروژه‌های متعددی با JSON کار کرده‌ام؛ از APIهای پرداخت گرفته تا فایل‌های تنظیمات میکروسرویس‌ها، و هر بار نکته‌ای تازه یاد گرفته‌ام که در مستندات رسمی جایی نداشت. اگر با مبانی این فرمت آشنایی ندارید، ابتدا JSON چیست و چطور داده‌ها را ساختاردهی می‌کند را بخوانید. این مقاله درباره چیزهایی است که در دنیای واقعی به آن‌ها برمی‌خورید.

چرا کار با JSON در پروژه واقعی متفاوت است؟

در یک آموزش مقدماتی، داده‌ای که parse می‌کنید تمیز است؛ ساختار مشخص، انواع داده درست و حجم کم. اما در پروژه واقعی، با چهار چیز متفاوت روبرو می‌شوید که هرکدام می‌توانند شما را ساعت‌ها درگیر کنند. اول، داده‌ای که از منابع خارجی می‌آید هیچ‌وقت به‌اندازه داده آزمایشی تمیز نیست. یک API خارجی ممکن است گاهی فیلدی را نفرستد، گاهی نوع داده را عوض کند و گاهی پاسخ ناقص بدهد. دوم، حجم داده با رشد پروژه به‌طور غیرخطی بزرگ می‌شود؛ چیزی که در ابتدا ۱۰ کیلوبایت بود، در سال دوم ممکن است به ۱۰ مگابایت برسد. سوم، تیم بزرگ‌تر می‌شود و قراردادهای نانوشته، به‌سرعت باعث ناسازگاری می‌شوند. چهارم، نیازهای امنیتی و مقرراتی وارد بازی می‌شوند.

تجربه‌ام می‌گوید بخش عمده مشکلات واقعی JSON از سه منبع می‌آید: خطای انسانی در طراحی، عدم اعتبارسنجی در مرزهای سیستم، و بی‌توجهی به تغییرات تدریجی. اگر API خودتان را با REST API در عمل طراحی می‌کنید، این سه منبع باید در ذهن‌تان پررنگ باشند. چون اگر از ابتدا این‌ها را در نظر نگیرید، رفع‌شان در آینده هزینه چندبرابری خواهد داشت.

JSON در آموزش، یک تمرین است؛ در پروژه واقعی، یک قرارداد. هرچه این قرارداد دقیق‌تر و شفاف‌تر باشد، هزینه نگهداری کمتر است.

تجزیه امن JSON: خطاهایی که بی‌صدا شکست می‌خورند

یکی از خطرناک‌ترین الگوهای کار با JSON، تجزیه بدون مدیریت خطاست. در جاوااسکریپت، JSON.parse() در صورت ورودی نامعتبر یک exception پرتاب می‌کند. اگر کد آن را try/catch نکند، کل تابع شکست می‌خورد. اما خطر بزرگ‌تر، در زبان‌هایی مثل PHP و پایتون است که توابع تجزیه، به‌جای پرتاب exception، مقدار false یا None برمی‌گردانند. اگر این مقدار با یک آرایه یا dict خالی اشتباه گرفته شود، باگ بی‌صدا رخ می‌دهد که تشخیصش بسیار سخت است.

// JavaScript — همیشه try/catch
let data;
try {
  data = JSON.parse(input);
} catch (e) {
  console.error("Invalid JSON:", e.message, "Input:", input.slice(0, 200));
  throw new Error("Malformed payload");
}

// PHP — همیشه خطا را چک کنید
$data = json_decode($input, true);
if (json_last_error() !== JSON_ERROR_NONE) {
    throw new RuntimeException(
        "JSON error: " . json_last_error_msg()
    );
}

یک نکته که در پروژه‌های واقعی بارها به کارم آمده: هنگام لاگ کردن خطای JSON، همیشه چند کاراکتر اول ورودی را هم ثبت کنید. بسیاری از خطاهای parse از یک کاراکتر BOM در ابتدای فایل یا یک newline اضافه می‌آیند. اگر فقط پیام خطا را لاگ کنید، ساعت‌ها وقت صرف حدس زدن می‌کنید؛ اگر چند بایت اول را ببینید، در چند ثانیه ریشه را پیدا می‌کنید.

خطای رایج دیگر، تجزیه دوگانه است. گاهی داده از دو لایه عبور می‌کند و یک‌بار به اشتباه به‌صورت رشته در یک فیلد ذخیره می‌شود. نتیجه، JSON داخل JSON است و parse اول به شما یک رشته می‌دهد که هنوز JSON است. برای تشخیص این حالت، همیشه بعد از parse یک بررسی نوع انجام دهید: اگر مقدار یک فیلد رشته بود ولی انتظار object داشتید، احتمالاً دوباره encode شده است.

پاک کردن BOM قبل از parse

فایل‌هایی که از ویندوز یا ابزارهای خاصی می‌آیند، ممکن است در ابتدای خود سه بایت BOM داشته باشند. تجزیه‌کننده‌های استاندارد JSON این بایت‌ها را نمی‌شناسند و خطا می‌دهند. راه‌حل: قبل از parse، کاراکتر BOM را حذف کنید. در PHP:

$content = file_get_contents($path);
if (substr($content, 0, 3) === "xEFxBBxBF") {
    $content = substr($content, 3);
}
$data = json_decode($content, true);

در جاوااسکریپت و Node.js هم می‌توانید با input.replace(/^uFEFF/, '') این کار را انجام دهید. اگر با فایل‌های CSV یا سایر فرمت‌های متنی هم کار می‌کنید، مقاله مدیریت فایل‌های CSV در پایتون و اکسل نکات مشابهی دارد که ارزش دیدن دارد.

اعتبارسنجی JSON: از چک ساده تا JSON Schema

تجزیه موفق JSON فقط یعنی داده از نظر نحوی درست است. اما داده درست از نظر نحوی می‌تواند از نظر معنایی اشتباه باشد؛ مثلاً فیلد email مقدار یک عدد داشته باشد یا فیلد created_at غایب باشد. اعتبارسنجی، لایه‌ای است که بعد از parse و قبل از استفاده از داده انجام می‌شود.

در پروژه‌های کوچک، اعتبارسنجی دستی کافی است: بررسی وجود فیلدها و نوع‌شان با دستورات شرطی. اما در پروژه‌های بزرگ، این کار به سرعت غیرقابل نگهداری می‌شود. راه‌حل استاندارد، JSON Schema است. یک schema، ساختار انتظار‌شده داده شماست: چه فیلدهایی اجباری هستند، چه نوعی دارند، چه محدودیت دامنه‌ای دارند. ابزارهای مختلفی مثل Ajv برای جاوااسکریپت و Opis برای PHP از این استاندارد پشتیبانی می‌کنند.

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "required": ["id", "email"],
  "properties": {
    "id": { "type": "integer", "minimum": 1 },
    "email": { "type": "string", "format": "email" },
    "age": { "type": "integer", "minimum": 18, "maximum": 120 }
  },
  "additionalProperties": false
}

نکته‌ای که در پروژه‌های واقعی زیاد دیده‌ام: اعتبارسنجی باید در مرزهای سیستم انجام شود؛ یعنی در ورودی‌های خارجی مثل API، فایل‌های آپلودی، و پیام‌های بین میکروسرویس‌ها. در داخل کد خودتان، اعتبارسنجی مضاعف معمولاً غیرضروری است و فقط کد را شلوغ می‌کند. اگر داده از یک ماژول داخلی می‌آید و شما به آن ماژول اعتماد دارید، اعتبارسنجی مجدد هزینه بی‌دلیل است. اما اگر داده از کتابخانه‌ای می‌آید که خودتان کنترلش نمی‌کنید، اعتبارسنجی در مرز ضروری است.

یکی از الگوهایی که در پروژه‌های بزرگ به کارم آمده، ترکیب اعتبارسنجی با لاگ‌گیری است. هر بار که داده‌ای در مرز سیستم رد می‌شود، یک رکورد لاگ ثبت می‌شود که شامل نمونه داده، schema مورد انتظار و علت رد است. این لاگ‌ها چند ماه بعد، زمانی که یک کلاینت جدید شکایت می‌کند، ارزش طلا دارند. برای مطالعه بیشتر درباره طراحی API که این اصول در آن رعایت شده، REST را عمیق بشناسید را ببینید.

اعتبارسنجی، نه دشمن سرعت است و نه لوکس؛ بیمه‌نامه‌ای است که هزینه‌اش را فقط یک بار می‌پردازید و در طول عمر پروژه سود می‌کنید.

طراحی ساختار JSON برای پروژه‌های بزرگ

ساختار JSON در پروژه کوچک، معمولاً اتفاقی و بر اساس نیاز لحظه‌ای طراحی می‌شود. اما در پروژه بزرگ، همین ساختار تبدیل به قرارداد بین چند تیم می‌شود. اگر از ابتدا اصولی طراحی نشود، اضافه کردن فیلد جدید یا تغییر ساختار، به فاجعه تبدیل می‌شود.

چهار اصل را در طراحی ساختار JSON رعایت می‌کنم. اول، ثبات نام‌گذاری: اگر در یک endpoint از created_at استفاده می‌کنید، در همه جا همان را به کار ببرید، نه createdAt یا created_date. دوم، نبود تناقض در تودرتویی: اگر لیست سفارش‌ها به‌صورت orders برگردانده می‌شود، در endpoint دیگر نباید order_list باشد. سوم، محدودیت عمق تودرتویی: به‌ندرت پیش می‌آید که بیش از سه سطح تودرتویی لازم باشد؛ اگر بیشتر شد، احتمالاً طراحی اشتباه است. چهارم، جداسازی داده و متادیتا: داده اصلی در یک فیلد مشخص مثل data و اطلاعات جانبی مثل صفحه‌بندی در meta.

الگومزیتمعایب
ساختار مستقیمسبک، سادهاضافه کردن متادیتا سخت می‌شود
wrapper با data/metaتوسعه‌پذیر، ثابتحجم بیشتر
wrapper با status/data/errorیکسان‌سازی پاسخ‌هاکد اضافه در کلاینت
JSON:API specificationاستاندارد، ابزار آمادهیادگیری و پیچیدگی اولیه

تجربه‌ام این است که برای پروژه‌های کوچک و متوسط، الگوی wrapper با data و meta بهترین تعادل را دارد. برای پروژه‌های سازمانی که چند تیم روی آن کار می‌کنند، پیاده‌سازی JSON:API ارزش یادگیری اولیه را دارد. اگر به دنبال مقایسه ساختاری با GraphQL هستید، GraphQL یا REST به شما دید بهتری می‌دهد.

مشکل تودرتویی عمیق و راه‌حل‌های عملی

یکی از شایع‌ترین اشتباهات در طراحی JSON، استفاده از تودرتویی عمیق برای نشان دادن روابط است. مثلاً یک سفارش که داخلش محصولات، داخل هر محصول دسته‌بندی، داخل هر دسته‌بندی زیردسته و داخل هر زیردسته ویژگی‌ها. اگر مصرف‌کننده فقط به اطلاعات سفارش نیاز داشته باشد، باید کل این درخت را دریافت و پیمایش کند.

راه‌حل اول، جداسازی موجودیت‌ها به endpoint مستقل است. سفارش فقط با شناسه محصولات برگردانده شود، و اگر کلاینت جزئیات محصول را خواست، از endpoint دیگری بگیرد. این کار حجم پاسخ اولیه را کاهش می‌دهد و به کلاینت کنترل می‌دهد چه چیزی را بارگذاری کند. راه‌حل دوم، استفاده از فیلدهای انتخاب (sparse fieldsets) است؛ یعنی کلاینت با پارامتری مثل ?fields=id,title مشخص کند چه فیلدهایی را می‌خواهد. این تکنیک به‌ویژه در APIهای عمومی که مصرف‌کنندگان متنوعی دارند، بسیار مفید است.

راه‌حل سوم، پهن کردن ساختار است. اگر یک رابطه یک-به-یک است، به‌جای یک object تودرتو، فیلدها را در سطح بالا قرار دهید. مثلاً {"author": {"name": "Ali"}} می‌تواند به {"author_name": "Ali"} تبدیل شود. این تغییر کوچک، هم حجم را کم می‌کند و هم دسترسی به فیلد را ساده‌تر می‌کند. اما اگر رابطه یک-به-چند یا چند-به-چند است، تودرتویی منطقی است.

کاراکترهای یونیکد، فارسی و مشکلات encoding

JSON به‌طور ذاتی از UTF-8 پشتیبانی می‌کند، اما در پروژه‌های واقعی، مشکلات encoding بی‌شمارند. رایج‌ترین آن، ورودی‌های غیر UTF-8 است. مثلاً فایلی که با Windows-1256 ذخیره شده، اگر مستقیم به json_decode() در PHP بدهید، مقدار false برمی‌گرداند. راه‌حل: قبل از parse، انکودینگ را تشخیص و به UTF-8 تبدیل کنید.

نکته دوم، escape کردن کاراکترهای فارسی در خروجی است. به‌صورت پیش‌فرض، بسیاری از زبان‌ها کاراکترهای یونیکد را به uXXXX تبدیل می‌کنند که حجم را تا سه برابر افزایش می‌دهد و خوانایی را کاهش می‌دهد. در PHP با پرچم JSON_UNESCAPED_UNICODE و در پایتون با ensure_ascii=False این مشکل حل می‌شود. برای متن فارسی این تنظیم ضروری است.

// PHP
$json = json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);

# Python
json_str = json.dumps(data, ensure_ascii=False)

نکته سوم، کاراکترهای کنترلی است. کاراکترهایی مثل tab، newline و carriage return باید در رشته‌های JSON escape شوند. اگر داده از یک منبع خارجی بیاید و این کاراکترها escape نشده باشند، parse خطا می‌دهد. راه‌حل: همیشه قبل از encode، رشته‌ها را با یک تابع sanitize پاک کنید.

نکته چهارم، کاراکترهای خاص فارسی مثل نیم‌فاصله (ZWNJ) است. این کاراکتر به‌صورت U+200C در یونیکد ذخیره می‌شود و در JSON مشکلی ایجاد نمی‌کند، اما در صورت تبدیل نادرست انکودینگ، ممکن است به یک کاراکتر نامرئی عجیب تبدیل شود که رفتار غیرمنتظره‌ای نشان می‌دهد. همیشه پس از تبدیل، یک تست round-trip انجام دهید: داده را encode کنید، دوباره decode کنید و با داده اصلی مقایسه کنید.

کاراکترهای یونیکد، مثل مهمان‌های خاموش هستند؛ اگر جای درست ننشینند، همه‌چیز را به‌هم می‌ریزند. همیشه بعد از encode، دوباره decode کنید و نتیجه را با داده اصلی بسنجید.

تاریخ، زمان و اعداد بزرگ در JSON

دو مورد از ظریف‌ترین دام‌های JSON، مربوط به تاریخ و اعداد بزرگ است. درباره تاریخ، استاندارد امروز ISO 8601 با UTC است: "2025-03-15T10:30:00Z". یک اشتباه رایج، ذخیره تاریخ به‌صورت timestamp عددی است. اگر این عدد بر حسب ثانیه باشد اما کلاینت آن را میلی‌ثانیه تفسیر کند، تاریخ به سال ۱۹۷۰ یا سال ۵۰۰۰۰ منتقل می‌شود. همیشه در مستندات ذکر کنید که timestamp بر حسب ثانیه است یا میلی‌ثانیه؛ ترجیحاً از ISO 8601 استفاده کنید که این ابهام را ندارد.

درباره اعداد بزرگ، داستان پیچیده‌تر است. جاوااسکریپت اعداد را به‌صورت double precision ذخیره می‌کند که فقط ۵۳ بیت دقت دارد. اگر API شما شناسه‌ای با ۶۴ بیت برگرداند، در سمت کلاینت دقت از دست می‌رود. مثلاً عدد ۹۰۰۷۱۹۹۲۵۴۷۴۰۹۹۳ را جاوااسکریپت به ۹۰۰۷۱۹۹۲۵۴۷۴۰۹۹۲ گرد می‌کند و نتیجه یک شناسه اشتباه است. راه‌حل: شناسه‌های بزرگ را به‌صورت رشته ارسال کنید. این کار در Twitter API و چند API بزرگ دیگر رعایت شده است.

// اشتباه — ممکن است دقت از دست برود
{ "id": 9007199254740993 }

// درست — به‌صورت رشته
{ "id": "9007199254740993" }

یک راه‌حل جایگزین، استفاده از کتابخانه‌های BigInt در سمت کلاینت است. اما این نیازمند تنظیمات خاص و پشتیبانی در همه کتابخانه‌های parse است. در عمل، تبدیل به رشته ساده‌ترین و قابل اعتمادترین راه است. اگر در حال طراحی API برای سیستم‌هایی هستید که ممکن است با جاوااسکریپت کار کنند، از ابتدا شناسه‌ها را رشته در نظر بگیرید.

بهینه‌سازی حجم و پردازش JSON

عملکرد JSON در پروژه واقعی دو لایه دارد: حجم داده منتقل‌شده و زمان پردازش. در سمت حجم، سه تکنیک اصلی وجود دارد. اول، حذف فیلدهای غیرضروری؛ اگر کلاینت فقط به پنج فیلد از بیست فیلد نیاز دارد، چرا کل بیست فیلد را بفرستید. دوم، فشرده‌سازی با gzip یا brotli؛ این کار حجم را در پاسخ‌های بزرگ تا ۸۰ درصد کاهش می‌دهد. سوم، کاهش تودرتویی؛ تودرتویی زیاد، حجم را چند برابر می‌کند بدون افزودن اطلاعات.

در سمت پردازش، هزینه parse به حجم و عمق داده بستگی دارد. تجربه‌ام نشان می‌دهد که در Node.js، پردازش یک JSON ۵۰۰ کیلوبایتی حدود ۱۰ تا ۱۵ میلی‌ثانیه طول می‌کشد. اگر سرور شما در هر ثانیه چند صد درخواست دریافت می‌کند، این هزینه جمع می‌شود. راه‌حل، کاهش حجم پاسخ با صفحه‌بندی و انتخاب فیلد است. یک قاعده سرانگشتی: هیچ پاسخ API نباید به‌طور معمول بیش از ۱۰۰ کیلوبایت باشد. اگر بیشتر شد، احتمالاً نیاز به صفحه‌بندی یا فیلتر دارید.

یک نکته که در پروژه‌های واقعی اهمیت دارد: همیشه قبل از بهینه‌سازی، اندازه‌گیری کنید. ابزارهایی مثل Chrome DevTools، curl --write-out و پروفایلرهای سرور به شما می‌گویند گلوگاه واقعی کجاست. بدون اندازه‌گیری، بهینه‌سازی فقط حدس است. اگر به دنبال بهینه‌سازی جدی در API هستید، اشتباهات رایج در REST API فهرست خوبی از مسائل مشترک را بررسی می‌کند.

پردازش streaming برای داده‌های بزرگ

وقتی داده از یک حدی بزرگ‌تر شود، parse کامل آن در حافظه کارایی ندارد. در پروژه‌هایی که با فایل‌های چند گیگابایتی یا پاسخ‌های بسیار بزرگ کار می‌کنم، از streaming parser استفاده می‌کنم. در Node.js، کتابخانه stream-json این امکان را می‌دهد که داده را به‌صورت تکه‌تکه بخوانید و پردازش کنید. در پایتون، ijson همین نقش را دارد و با آن می‌توانید یک فایل JSON بزرگ را بدون بارگذاری کامل در حافظه پیمایش کنید.

// Python — ijson
import ijson

with open("large.json", "rb") as f:
    for record in ijson.items(f, "items.item"):
        process(record)  # هر رکورد به‌جدا پردازش می‌شود

در PHP هم می‌توانید از کتابخانه‌هایی مثل halaxa/json-machine استفاده کنید. اما یک هشدار مهم: streaming parse پیچیدگی بیشتری دارد و کد را سخت‌تر می‌کند. اگر داده شما کمتر از چند مگابایت است، به‌سراغ streaming نروید. streaming زمانی ارزش دارد که داده بزرگ‌تر از حافظه در دسترس باشد یا سرعت پردازش اولیه مهم باشد.

در سمت تولید داده، اگر می‌خواهید پاسخ‌های بزرگ را به‌صورت streaming بفرستید، پروتکل HTTP chunked transfer encoding مناسب است. اما در عمل، این تکنیک بیشتر در گزارش‌گیری و صادرات داده استفاده می‌شود و در APIهای عمومی کمتر دیده می‌شود.

دام‌های امنیتی پنهان در JSON

JSON به‌خودی‌خود امن است چون کد اجرا نمی‌کند. اما چهار دام امنیتی وجود دارد که در پروژه‌های واقعی زیاد دیده‌ام. اول، Prototype Pollution در جاوااسکریپت. اگر داده JSON دریافتی را با توابعی مثل deep merge ترکیب کنید، مهاجم می‌تواند از طریق کلیدهای __proto__ یا constructor، prototype object را آلوده کند. راه‌حل: قبل از merge، این کلیدها را فیلتر کنید یا از کتابخانه‌های امن استفاده کنید.

دام دوم، انفجار حجم و عمق. یک payload بزرگ یا ساختار تو‌در‌توی بسیار عمیق می‌تواند سرور را در مرحله parse از پا دربیاورد. راه‌حل: تعیین سقف حجم payload و محدود کردن عمق ساختار JSON قبل از parse. در بسیاری از فریم‌ورک‌ها این محدودیت به‌صورت پیش‌فرض وجود دارد اما همیشه بررسی کنید که فعال است.

دام سوم، XSS از طریق JSON. اگر داده JSON حاوی کاراکترهای HTML باشد و مستقیم در DOM تزریق شود، می‌تواند باعث XSS شود. برای مثال، اگر یک API پاسخ {"name": "<script>alert(1)</script>"} بدهد و شما آن را با innerHTML درج کنید، اسکریپت اجرا می‌شود. راه‌حل: همیشه از textContent استفاده کنید یا داده را از طریق escaping پاک کنید.

دام چهارم، افشای داده حساس. اگر در طراحی ساختار JSON دقت نکنید، ممکن است فیلدهای حساس مثل توکن، رمز و اطلاعات داخلی در پاسخ قرار بگیرند. همیشه قبل از انتشار API، ساختار پاسخ را بازبینی کنید و فیلدهای حساس را حذف کنید. یک راه‌حل، استفاده از DTO است که داده را قبل از خروج از سیستم فیلتر می‌کند. اگر با قالب یا سیستم‌های وب آشناتر شوید، REST API در وردپرس نشان می‌دهد که این پلتفرم چطور با مسئله افشای داده برخورد می‌کند.

نسخه‌بندی و سازگاری ساختار JSON

هر ساختار JSON در طول زمان تغییر می‌کند. اگر این تغییرات مدیریت نشوند، کلاینت‌های قدیمی می‌شکنند. سه قاعده ساده برای سازگاری را در همه پروژه‌ها رعایت می‌کنم. اول، اضافه کردن فیلد جدید هرگز فیلد قبلی را حذف نمی‌کند. دوم، حذف فیلد در نسخه جدید انجام می‌شود، نه در نسخه فعلی. سوم، تغییر نوع داده همیشه نسخه‌بندی می‌شود.

در طراحی، سعی کنید به فیلدهای فعلی معناهای اضافه ندهید که ممکن است بعداً نیاز به تغییر داشته باشند. مثلاً اگر فیلد status را به‌عنوان رشته با مقادیر محدود استفاده می‌کنید، اضافه کردن یک مقدار جدید در آینده می‌تواند کلاینت‌های قدیمی را که فقط دو مقدار را می‌شناسند بشکند. راه‌حل: مستندسازی دقیق و ارتباط فعال با مصرف‌کنندگان API.

نکته دیگر، استفاده از null در مقابل حذف فیلد است. اگر می‌خواهید یک فیلد در شرایط خاص وجود نداشته باشد، بهتر است آن را با مقدار null بفرستید تا اینکه کاملاً حذف کنید. کلاینت‌ها کدی که برای null آماده است، راحت‌تر می‌توانند با آن کنار بیایند.

تست سازگاری

یک تکنیک عملی که در پروژه‌ها استفاده می‌کنم: تست قرارداد (contract testing). ابزارهایی مثل Pact یا Postman می‌توانند قرارداد JSON بین سرور و کلاینت را ثبت و بررسی کنند. اگر تغییری در سرور باعث شکستن قرارداد شود، تست قبل از انتشار هشدار می‌دهد. این کار، هزینه تغییرات را از بعد از انتشار به قبل از انتشار منتقل می‌کند.

ابزارها و کتابخانه‌های ضروری برای کار با JSON

در طول سال‌ها کار با JSON، مجموعه‌ای از ابزارها را جمع کرده‌ام که در پروژه‌های مختلف ارزش زیادی داشته‌اند. در سمت توسعه و دیباگ، jq ابزاری است که هر توسعه‌دهنده باید بداند. با jq می‌توانید در خط فرمان، JSON را فیلتر، مرتب و خلاصه کنید. برای مثال، curl api | jq '.items[] | {id, name}' فقط فیلدهای دلخواه را نمایش می‌دهد.

در سمت اعتبارسنجی، Ajv برای جاوااسکریپت، Opis برای PHP و jsonschema برای پایتون ابزارهای اصلی هستند. این کتابخانه‌ها از JSON Schema پشتیبانی می‌کنند و امکان اعتبارسنجی سریع را می‌دهند. در سمت Node.js، zod یک انتخاب مدرن است که با TypeScript کار می‌کند و تجربه بهتری برای توسعه‌دهنده فراهم می‌کند.

در سمت انکودینگ و تبدیل، ابزارهایی مثل jq، json_pp و آنلاین‌هایی مثل JSONLint برای بررسی صحت JSON مفیدند. برای کار با فایل‌های بزرگ، jq و کتابخانه‌های streaming که قبلاً ذکر شد بهترین گزینه هستند. در سمت پایگاه داده، اگر از PostgreSQL استفاده می‌کنید، نوع داده jsonb امکان کوئری مستقیم روی JSON را می‌دهد که ترکیب قدرتمندی است. اگر به پایگاه‌های NoSQL علاقه‌مندید، چه زمانی از NoSQL استفاده کنیم نکات مهمی درباره انتخاب دارد.

ابزار خوب، سرعت کار را دو برابر می‌کند؛ اما فقط اگر ابزار مناسب برای کار مناسب را انتخاب کنید. jq برای فایل خطی، ijson برای فایل بزرگ، و Ajv برای اعتبارسنجی؛ هرکدام جایگاه خود را دارند.

پرسش‌های پرتکرار درباره کار با JSON

چرا JSON.parse در Node.js روی داده‌ای که معتبر به‌نظر می‌رسد خطا می‌دهد؟
رایج‌ترین دلایل: وجود BOM در ابتدای داده، کاما انتهایی، کاراکتر کنترلی بدون escape، یا deep nesting بیش از حد مجاز. اول BOM را چک کنید، بعد ساختار را با یک ابزار اعتبارسنجی بررسی کنید، و در نهایت خطای دقیق را لاگ کنید. در Node.js، پیام خطا معمولاً موقعیت خطای دقیق را نشان می‌دهد که کمک بزرگی است.

آیا بهتر است داده را به‌صورت رشته در پایگاه داده ذخیره کنم یا فیلدهای جداگانه؟
این تصمیم به نوع کاربرد بستگی دارد. اگر با ساختار ثابت و کوئری‌های متعدد روی فیلدها کار می‌کنید، فیلدهای جداگانه بهتر است. اگر ساختار انعطاف‌پذیر و کوئری کمتر است، ذخیره به‌صورت JSON منطقی است. PostgreSQL با نوع jsonb امکان کوئری روی داده JSON را فراهم می‌کند که مزیت هر دو حالت را می‌دهد.

چگونه یک فایل JSON چند گیگابایتی را در PHP پردازش کنم؟
پردازش کامل در حافظه منطقی نیست. از کتابخانه‌هایی مثل halaxa/json-machine استفاده کنید که امکان streaming parse را می‌دهد. اگر خیلی بزرگ است، بهتر است قبل از پردازش، فایل را به بخش‌های کوچک‌تر تقسیم کنید یا از یک پایگاه داده مناسب استفاده کنید.

آیا استفاده از camelCase یا snake_case برای کلیدها تفاوت مهمی دارد؟
از نظر JSON هیچ تفاوتی ندارد، چون کلیدها فقط رشته هستند. از نظر کد سمت کلاینت، تفاوت مهم است. اکثر APIهای جاوااسکریپتی از camelCase و APIهای پایتون و PHP از snake_case استفاده می‌کنند. مهم‌ترین چیز، ثبات در کل API است.

چگونه حجم پاسخ JSON را کاهش دهم بدون اینکه چیزی از دست برود؟
سه کار موثر: اول، فشرده‌سازی با gzip یا brotli که حجم را تا ۸۰ درصد کم می‌کند. دوم، حذف فیلدهای non-essential از پاسخ. سوم، صفحه‌بندی به‌جای ارسال همه رکوردها. ترکیب این سه، معمولاً پاسخ را از چند صد کیلوبایت به چند ده کیلوبایت می‌رساند.

آیا باید از JSON Schema برای همه endpointها استفاده کنم؟
برای پروژه‌های کوچک، ممکن است اضافه‌کاری باشد. برای پروژه‌های متوسط و بزرگ، توصیه می‌کنم حداقل برای ورودی‌های حیاتی از Schema استفاده کنید. Schema، مستندسازی را خودکار، اعتبارسنجی را یکنواخت و کد را قابل تست‌تر می‌کند. هزینه اولیه یادگیری، در طول عمر پروژه چند برابر برمی‌گردد.

چطور بفهمم JSON از یک API خارجی امن است؟
سه چک اصلی: اول، Content-Type پاسخ باید application/json باشد. دوم، اندازه پاسخ باید محدود باشد؛ اگر پاسخ بزرگ است، آن را بلوکه کنید یا محدودیت حجم بگذارید. سوم، داده را در مرز سیستم اعتبارسنجی کنید، حتی اگر API خارجی معتبر است. هرگز به داده خارجی بدون اعتبارسنجی اعتماد نکنید.

تفاوت JSON و JSON5 و JSONC چیست؟
JSON استاندارد رسمی است که کامنت، trailing comma و کلید بدون گیومه را پشتیبانی نمی‌کند. JSON5 یک نسخه تعمیم‌یافته است که این‌ها را پشتیبانی می‌کند و بیشتر در فایل‌های پیکربندی استفاده می‌شود. JSONC فقط کامنت را به JSON اضافه می‌کند و در ابزارهایی مثل VS Code استفاده می‌شود. هیچ‌کدام جایگزین JSON استاندارد در APIها نیستند.

درس‌هایی که در میدان یاد گرفتم

اگر بخواهم چند نکته کلیدی این مقاله را در یک پاراگراف خلاصه کنم، باید بگویم: JSON یک فرمت ساده است اما پروژه‌های واقعی پیچیده‌اند. مدیریت خطای دقیق، اعتبارسنجی در مرزهای سیستم، طراحی ساختار با دید بلندمدت، توجه به یونیکد و اعداد بزرگ، بهینه‌سازی حجم، و رعایت اصول امنیتی، همه بخشی از کار حرفه‌ای با JSON هستند. تجربه‌ام می‌گوید کسانی که این اصول را از ابتدا رعایت می‌کنند، در سال دوم و سوم پروژه، خیلی کمتر درگیر باگ‌های پنهان می‌شوند.

ساده‌ترین قدمی که هر توسعه‌دهنده می‌تواند بردارد این است: قبل از نوشتن هر خط کد که JSON parse می‌کند، یک بار بپرسید اگر داده نامعتبر بود چه اتفاقی می‌افتد. اگر پاسخ این سوال را از قبل می‌دانید، نصف راه را رفته‌اید. برای یادگیری عمیق‌تر این مفاهیم و سایر موضوعات مرتبط با داده و API، پیشنهاد می‌کنم روی مجموعه مقالات دسته‌بندی‌شده ما در بخش توسعه وب وقت بگذارید.

اگر در پروژه‌تان با دام خاصی از JSON روبرو شده‌اید که در این فهرست نبود، یا ترفندی می‌شناسید که کار را ساده‌تر می‌کند، خوشحال می‌شوم در دیدگاه‌ها بخوانم. تجربه‌های واقعی از میدان، همیشه ارزشمندترین بخش یک مقاله فنی هستند.