در JSONهای کوچک، ترتیب کلیدها معمولاً مسئله مهمی به نظر نمیرسد؛ اما وقتی داده وارد Git، تست Snapshot، فرایند Review، تولید مستندات یا مقایسه نسخهها میشود، جابهجایی تصادفی کلیدها میتواند صدها خط اختلاف بیمعنا ایجاد کند. مرتبسازی کلیدهای JSON یا Sort JSON Keys روشی برای ایجاد یک نمایش پایدار و قابل پیشبینی از Objectها است. این کار باید بدون تغییر مقدارها، نوع دادهها و ترتیب Arrayها انجام شود.
Quick Answer: مرتبسازی کلیدهای JSON یعنی Propertyهای هر Object را براساس یک قاعده مشخص، معمولاً ترتیب الفبایی، بازچینی کنیم. این عملیات فقط نمایش و ترتیب کلیدهای Object را تغییر میدهد و نباید عناصر Array، مقدارها یا نوع دادهها را دستکاری کند. برای خروجی پایدار، باید مشخص شود مرتبسازی فقط در سطح ریشه است یا بهصورت بازگشتی روی همه Objectهای تودرتو اجرا میشود.
مرتبسازی کلیدهای JSON چیست؟
JSON از Object، Array و مقدارهای اولیه ساخته میشود. Object مجموعهای از جفتهای کلید و مقدار است و Array یک فهرست ترتیبی از عناصر. Sort JSON Keys فقط روی کلیدهای Object اثر میگذارد. برای مثال، Object زیر از نظر دادهای با نسخه مرتبشده خود یکسان است، اما نمایش دوم برای مقایسه و خواندن پایدارتر است.
{
"status": "active",
"id": 42,
"name": "Sara"
}
{
"id": 42,
"name": "Sara",
"status": "active"
}
این تبدیل با Format کردن فرق دارد. Formatter فاصله، تورفتگی و خط جدید را تنظیم میکند؛ Sorter ترتیب Propertyها را تغییر میدهد. بسیاری از ابزارها هر دو کار را همزمان انجام میدهند، اما از نظر مفهومی دو عملیات مستقل هستند.
آیا ترتیب کلیدها در JSON معنا دارد؟
در بیشتر کاربردهای JSON، مصرفکننده باید Object را براساس نام کلید بخواند، نه موقعیت آن. بنابراین تغییر ترتیب کلیدهای یک Object نباید معنای داده را عوض کند. با این حال ترتیب میتواند برای نمایش، Hash متنی، امضای دیجیتال، Snapshot، فایلهای ذخیرهشده و برخی پیادهسازیهای ناسازگار اهمیت عملی پیدا کند.
نکته مهم این است که «نباید به ترتیب Object وابسته بود» با «ترتیب در متن وجود ندارد» یکسان نیست. متن JSON یک توالی مشخص دارد و Parserها اغلب همان ترتیب ورودی را در ساختار داخلی حفظ میکنند، اما منطق برنامه نباید نتیجه کسبوکار را به آن وابسته کند مگر قرارداد صریحی وجود داشته باشد.
Object با Array چه تفاوتی دارد؟
| ساختار | آیا ترتیب بخشی از معناست؟ | آیا باید مرتب شود؟ |
|---|---|---|
| Object | معمولاً خیر؛ دسترسی با نام کلید انجام میشود | در صورت نیاز به خروجی پایدار، بله |
| Array | بله؛ موقعیت عناصر مهم است | فقط با دستور صریح و منطق دامنه |
مرتبسازی کلیدهای JSON هرگز مجوز مرتبکردن Array نیست. اگر Array شامل مراحل یک فرایند، رتبهبندی، مختصات یا تاریخچه باشد، تغییر ترتیب آن میتواند داده را خراب کند.
چرا کلیدهای JSON را مرتب میکنیم؟
کاهش اختلافهای بیمعنا در Git
دو برنامه ممکن است همان Object را با ترتیب متفاوت Serialize کنند. اگر فایل در مخزن کد ذخیره شود، Git تغییرات زیادی نشان میدهد، در حالی که مقدار واقعی تغییر نکرده است. مرتبسازی بازگشتی پیش از Commit باعث میشود Diff فقط تغییرهای واقعی را نمایش دهد.
پایدارکردن Snapshot Test
Snapshotها خروجی متنی را ذخیره میکنند. ترتیب ناپایدار کلیدها میتواند تست را بدون تغییر منطقی شکست دهد. Canonical order یا ترتیب ثابت، خروجی را قابل تکرار میکند. البته Snapshot نباید جای تست معنایی را بگیرد؛ مرتبسازی فقط نویز را کم میکند.
مقایسه دو JSON
برای مقایسه متنی، ترتیب متفاوت کلیدها باعث اختلاف ظاهری میشود. مرتبسازی هر دو ورودی با یک قاعده مشترک، مقایسه را دقیقتر میکند. برای مقایسه ساختاری بهتر است از ابزار مقایسه دو JSON استفاده شود، زیرا آن ابزار میتواند تفاوت ترتیب Object را نادیده بگیرد و تغییر مقدار را جداگانه نشان دهد.
خوانایی و استاندارد تیمی
بعضی تیمها کلیدهای اصلی مانند id، type و name را در ابتدا نگه میدارند و بقیه را الفبایی مرتب میکنند. این سبک خواندن فایلهای تنظیمات را سریعتر میکند. اگر چنین قاعدهای دارید باید آن را مستند و خودکار کنید؛ اجرای دستی بهمرور ناسازگار میشود.
مرتبسازی سطح ریشه و مرتبسازی بازگشتی
مرتبسازی سطح ریشه فقط کلیدهای Object اصلی را جابهجا میکند. Objectهای داخلی دستنخورده میمانند. مرتبسازی بازگشتی یا Deep Sort در تمام Objectهای تودرتو اجرا میشود و Arrayها را فقط پیمایش میکند تا Objectهای داخل آنها نیز مرتب شوند.
{
"user": {
"status": true,
"id": 7,
"name": "Mina"
},
"meta": {
"version": 2,
"createdAt": "2026-08-06"
}
}
در Deep Sort، کلیدهای user و meta نیز براساس قاعده انتخابی بازچینی میشوند. اگر هدف تولید خروجی کاملاً پایدار است، مرتبسازی بازگشتی معمولاً انتخاب مناسبتری است.
قواعد مختلف مرتبسازی
| قاعده | نمونه رفتار | کاربرد | محدودیت |
|---|---|---|---|
| الفبایی صعودی | a سپس b سپس c | عمومی و قابل پیشبینی | ممکن است با زبان و Locale تغییر کند |
| الفبایی نزولی | c سپس b سپس a | گزارشهای خاص | کمتر رایج |
| Case-sensitive | حروف بزرگ و کوچک جدا | خروجی فنی دقیق | برای انسان غیرمنتظره است |
| Case-insensitive | Name نزدیک name | خوانایی انسانی | برای کلیدهای برابر از نظر Case نیاز به Tie-break دارد |
| Natural sort | item2 قبل از item10 | کلیدهای شمارهدار | پیادهسازی پیچیدهتر |
| اولویت سفارشی | id و name قبل از بقیه | قراردادهای تیمی | نیازمند فهرست و نگهداری |
Locale و Unicode
مرتبسازی Stringها همیشه یک نتیجه جهانی ندارد. مقایسه کدپوینت Unicode، مقایسه Locale-aware و مقایسه بدون حساسیت به Case میتوانند خروجیهای متفاوت ایجاد کنند. برای فایلهایی که باید در همه محیطها یکسان باشند، باید الگوریتم و Locale ثابت باشد. استفاده از Locale سیستم بدون تعیین صریح میتواند باعث شود خروجی روی دو سرور فرق کند.
کلیدهای فارسی
برای کلیدهای فارسی، تفاوت نویسههای مشابه مانند ی و ک، نیمفاصله، اعراب و شکلهای Unicode میتواند ترتیب را تغییر دهد. Normalize کردن Unicode ممکن است برای مقایسه مفید باشد، اما نباید نام کلید واقعی را بدون توافق تغییر دهد. بهتر است کلید اصلی حفظ شود و فقط کلید مقایسهای موقت Normalize شود.
مثال صحیح و غلط
روش صحیح
{
"customer": {
"email": "user@example.test",
"id": 15,
"name": "Reza"
},
"items": [
{ "price": 250000, "sku": "A-1" },
{ "price": 90000, "sku": "B-2" }
]
}
در این خروجی، کلیدهای Object مرتب شدهاند و ترتیب دو عنصر Array حفظ شده است.
روش غلط
{
"customer": {
"email": "user@example.test",
"id": 15,
"name": "Reza"
},
"items": [
{ "price": 90000, "sku": "B-2" },
{ "price": 250000, "sku": "A-1" }
]
}
اینجا عناصر Array براساس قیمت جابهجا شدهاند. این دیگر Sort Keys نیست و ممکن است ترتیب سفارش، اولویت یا منطق کسبوکار را تغییر دهد.
پیادهسازی مرتبسازی بازگشتی در JavaScript
function sortJsonKeys(value) {
if (Array.isArray(value)) {
return value.map(sortJsonKeys);
}
if (value !== null && typeof value === "object") {
return Object.keys(value)
.sort((a, b) => a.localeCompare(b, "en"))
.reduce((result, key) => {
result[key] = sortJsonKeys(value[key]);
return result;
}, {});
}
return value;
}
const sorted = sortJsonKeys(input);
const output = JSON.stringify(sorted, null, 2);
تابع ابتدا Array را تشخیص میدهد و ترتیب عناصر را حفظ میکند. سپس Objectها را با کلید مرتبشده بازسازی میکند. مقدارهای اولیه مانند String، Number، Boolean و null بدون تغییر برگردانده میشوند.
چرا نباید از typeof بهتنهایی استفاده کرد؟
در JavaScript، مقدار null نیز نوع object گزارش میشود و Array هم Object است. بنابراین باید Array و null قبل از Object معمولی بررسی شوند. نادیدهگرفتن این موضوع باعث خطا یا تبدیل اشتباه ساختار میشود.
پیادهسازی در Python
import json
def sort_json_keys(value):
if isinstance(value, list):
return [sort_json_keys(item) for item in value]
if isinstance(value, dict):
return {
key: sort_json_keys(value[key])
for key in sorted(value.keys())
}
return value
sorted_data = sort_json_keys(data)
output = json.dumps(
sorted_data,
ensure_ascii=False,
indent=2
)
در نسخههای مدرن Python، Dict ترتیب درج را حفظ میکند؛ بنابراین Dict جدیدی که با کلیدهای مرتب ساخته میشود، هنگام Serialization همان ترتیب را نمایش میدهد. گزینه ensure_ascii=False نیز متن فارسی را خواناتر نگه میدارد.
استفاده از sort_keys در json.dumps
کتابخانه استاندارد Python میتواند هنگام Serialization کلیدها را مرتب کند:
output = json.dumps(
data,
ensure_ascii=False,
indent=2,
sort_keys=True
)
این روش ساده است و بهصورت بازگشتی کلیدهای Dictionary را مرتب میکند. با این حال کنترل Natural Sort، اولویت سفارشی یا Locale محدود است. اگر قرارداد پیچیده دارید، بهتر است ساختار را پیش از Serialization با تابع اختصاصی مرتب کنید.
مرتبسازی کلیدها و JSON Canonicalization
مرتبسازی کلیدها با Canonicalization کامل یکی نیست. Canonicalization علاوه بر ترتیب کلیدها، ممکن است نمایش Number، Escape String، Unicode و فاصلهها را نیز استاندارد کند. این تفاوت برای Hash، Cache Key و امضای دیجیتال بسیار مهم است.
اگر فقط کلیدها را مرتب کنید اما یک سیستم عدد 1 و سیستم دیگر 1.0 تولید کند، متن نهایی هنوز متفاوت خواهد بود. برای کاربردهای امنیتی یا بینسیستمی باید از یک قرارداد Canonical مشخص استفاده شود، نه صرفاً Sort الفبایی.
تأثیر مرتبسازی بر Hash و امضا
Hash روی بایتها محاسبه میشود. دو JSON با معنای یکسان اما فاصلهگذاری یا ترتیب کلید متفاوت، Hash متنی متفاوت دارند. مرتبسازی میتواند بخشی از مسیر تولید ورودی پایدار باشد، اما باید تمام مراحل Serialization نیز ثابت باشند.
- Encoding باید مشخص باشد، معمولاً UTF-8.
- قانون Escape کاراکترها باید ثابت باشد.
- نمایش Number باید یکسان باشد.
- فاصله و خط جدید باید کنترل شود.
- الگوریتم ترتیب کلیدها باید دقیقاً مشترک باشد.
برای امضای داده، هرگز به رفتار پیشفرض دو کتابخانه متفاوت اعتماد نکنید. قرارداد Canonical را صریح تعریف و با Test Vector مشترک بررسی کنید.
مرتبسازی در فایلهای تنظیمات
در فایل Config، ترتیب گاهی برای خواننده مهم است. مرتبسازی کامل الفبایی ممکن است کلیدهای مرتبط را از هم جدا کند. برای مثال host، port و timeout ممکن است منطقی باشد کنار هم بمانند. در این شرایط سه راه وجود دارد:
- از ترتیب سفارشی با گروههای مشخص استفاده کنید.
- فقط بخشهای ماشینی فایل را مرتب کنید.
- بهجای Sort کامل، Formatter و Linter تیمی تعریف کنید.
هدف خروجی پایدار است، نه قربانیکردن خوانایی. یک قانون ثابت و مستند بهتر از مرتبسازی افراطی است.
خطاهای رایج هنگام Sort JSON Keys
مرتبکردن Arrayها بدون اجازه
این رایجترین خطاست. Array ساختاری ترتیبی است و جابهجایی عناصر میتواند معنا را تغییر دهد. ابزار Sort Keys فقط باید Objectهای داخل Array را مرتب کند، نه خود عناصر را.
تبدیل Number به String
برخی روشهای ساده همه مقدارها را برای مقایسه یا بازسازی به String تبدیل میکنند. نتیجه JSON از نظر Syntax معتبر است اما نوع داده خراب شده است. مقدارها باید دستنخورده بمانند.
مرتبسازی فقط سطح اول
اگر هدف Diff پایدار است، باقیماندن ترتیب تصادفی در Objectهای داخلی مشکل را حل نمیکند. گزینه Recursive باید مشخص و معمولاً فعال باشد.
استفاده از Locale نامشخص
خروجی ممکن است روی سیستم توسعه و سرور متفاوت شود. Locale و Comparator باید صریح باشند.
فرض برابر بودن Sort با Validation
مرتبشدن کلیدها هیچ تضمینی درباره معتبر بودن ساختار، وجود فیلدهای Required یا نوع مقدارها نمیدهد. پس از Sort، در صورت نیاز از JSON Validator یا JSON Schema استفاده کنید.
تکیه بر ترتیب برای منطق برنامه
کد مصرفکننده باید کلید را با نام بخواند. اگر اولین Property را بهعنوان فیلد اصلی در نظر بگیرد، با Sort شدن خروجی رفتار برنامه تغییر میکند؛ این نشانه طراحی شکننده است.
امنیت و حریم خصوصی
مرتبسازی کلیدها ذاتاً عملیات امنیتی نیست و داده را رمزنگاری یا ناشناس نمیکند. اگر JSON شامل Token، ایمیل، شناسه شخصی یا اطلاعات محرمانه است، واردکردن آن در ابزار نامطمئن میتواند ریسک داشته باشد. ابزار باید پردازش محلی یا سیاست روشن نگهداری داده داشته باشد.
- پیش از اشتراکگذاری خروجی، Secretها را حذف کنید.
- برای فایل بزرگ محدودیت اندازه و عمق اعمال کنید.
- از Parser امن استفاده کنید و متن را با eval اجرا نکنید.
- خروجی مرتبشده را بهاشتباه داده Sanitized تلقی نکنید.
- برای امضا و Hash از Canonicalization تعریفشده استفاده کنید.
عملکرد روی JSONهای بزرگ
مرتبسازی هر Object با n کلید معمولاً هزینهای نزدیک به n log n دارد. در ساختارهای تودرتو، هزینه کل به تعداد Objectها و توزیع کلیدها وابسته است. علاوه بر زمان Sort، ساخت نسخه جدید از داده میتواند حافظه زیادی مصرف کند.
برای فایل بزرگ، ابتدا محدودیت اندازه و عمق را بررسی کنید. اگر فقط خروجی متنی لازم است، بعضی Serializerها میتوانند کلیدها را هنگام نوشتن مرتب کنند. برای JSON بسیار بزرگ، Parser کامل ممکن است حافظه را اشغال کند و پردازش Streaming نیز دشوار است، چون برای مرتبکردن یک Object باید همه کلیدهای آن پیش از خروجی در دسترس باشند.
بهترین روشها
- قبل از Sort، JSON را Parse و خطاهای Syntax را مشخص کنید.
- Arrayها را فقط پیمایش کنید و ترتیب عناصر را تغییر ندهید.
- Recursive بودن عملیات را صریح انتخاب کنید.
- Comparator، Case sensitivity و Locale را مستند کنید.
- برای کلیدهای شمارهدار درباره Natural Sort تصمیم بگیرید.
- مقدارها و نوع دادهها را بدون تغییر نگه دارید.
- برای Hash یا امضا از Canonicalization کامل استفاده کنید.
- پس از تبدیل، خروجی را Parse و با ورودی بهصورت ساختاری مقایسه کنید.
چکلیست انتخاب ابزار مرتبسازی JSON
- آیا ابزار فقط Objectها را مرتب میکند و Array را حفظ میکند؟
- آیا Deep Sort قابل فعال یا غیرفعالکردن است؟
- آیا ترتیب صعودی، نزولی یا Natural پشتیبانی میشود؟
- آیا Unicode و کلیدهای فارسی بدون خرابشدن حفظ میشوند؟
- آیا Number، Boolean و null نوع خود را نگه میدارند؟
- آیا JSON نامعتبر قبل از تبدیل گزارش میشود؟
- آیا خروجی قابل کپی، دانلود و مقایسه است؟
- آیا محدودیت اندازه و حریم خصوصی روشن است؟
جمعبندی
مرتبسازی کلیدهای JSON روشی ساده اما مهم برای تولید خروجی پایدار، کاهش نویز Git، قابل اعتمادترشدن Snapshotها و مقایسه آسانتر داده است. این عملیات باید فقط ترتیب Propertyهای Object را تغییر دهد و ترتیب Array، مقدارها و نوع دادهها را حفظ کند. برای نتیجه قابل تکرار، قاعده Sort، Locale، Case sensitivity و Recursive بودن باید مشخص باشند. همچنین Sort Keys را نباید با Validation، Format یا Canonicalization امنیتی یکی دانست. برای مرتبسازی سریع و بازگشتی میتوانید از ابزار Sort JSON Keys استفاده کنید و سپس نتیجه را با Viewer یا Diff بررسی کنید.