وقتی یک کلید JSON دو مقدار دارد، کدام اجرا می‌شود؟

کلید تکراری ممکن است پیش از اعتبارسنجی از JSON ناپدید شود. ورودی خام را در مرز درست بررسی کنید تا بازبین و ابزار اجرایی از یک داده، دو برداشت متفاوت نسازند.

وقتی یک کلید JSON دو مقدار دارد، کدام اجرا می‌شود؟
نویسنده
تیم ژرف
انتشار
۱۷ مهر ۱۴۰۵
زمان مطالعه
۱۱ دقیقه

دستیار برای سفارش یک کالا، این متن را تحویل می‌دهد: {"quantity":2,"quantity":20}. بازبین مقدار ۲ را می‌بیند، ولی ابزار عدد ۲۰ را می‌خواند. هر دو عدد صحیح‌اند؛ حتی قاعده «تعداد باید بین ۱ و ۵۰ باشد» هم هیچ‌کدام را رد نمی‌کند. مشکل پیش از بررسی مقدار آغاز شده: دو جزء برنامه درباره اینکه مقدار این فیلد چیست توافق ندارند.

این مثال ساخته‌شده، رفتار قطعی همه تجزیه‌گرها یا گزارش حادثه‌ای واقعی نیست. پرسش راهنما برای سازنده ابزار روشن است: کجا باید نام‌های تکراری را رد کنیم تا اختلاف پیش از نمایش، تأیید یا اجرا از بین برود؟ انتخاب آخرین مقدار، راه‌حل پیش‌فرض این تصمیم نیست.

ابتدا متن ورودی را از شیء ساخته‌شده جدا کنید

متن JSON با شیئی که کتابخانه از آن می‌سازد یکی نیست. متن می‌تواند دو بار نام quantity را داشته باشد؛ دیکشنری نهایی شاید فقط یک خانه برای آن نگه دارد. وقتی یکی از مقدارها حذف شد، اعتبارسنجی که فقط دیکشنری را دریافت می‌کند دیگر شاهدی از تکرار ندارد. این حذف لزوماً خطا تولید نمی‌کند و در گزارش ظاهراً سالم برنامه هم دیده نمی‌شود.

در RFC 8259، یکتابودن نام اعضای شیء توصیه شده، نه اینکه دستور زبان هر نام تکراری را ممنوع کند. سند توضیح می‌دهد گیرنده‌ها ممکن است آخرین مقدار را نگه دارند، خطا بدهند یا همه جفت‌ها را گزارش کنند. پس «کتابخانه آن را خواند» معادل «همه اجزا یک معنا گرفتند» نیست.

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

بررسی تکرار باید پیش از پاک‌شدن شاهد انجام شود

مسیر دریافت را از ابتدا دنبال کنید: بدنه پاسخ، رمزگشایی متن، تجزیه JSON، ساخت شیء، اعتبارسنجی، تبدیل به نوع داخلی و اجرای ابزار. اولین جزء که نام‌های تکراری را روی هم می‌نویسد، مرز تعیین‌کننده است. افزودن بررسی در لایه‌ای بعد از آن، شاهد ازدست‌رفته را برنمی‌گرداند.

ممکن است بسته ارائه‌دهنده از قبل شیء آماده تحویل دهد. در این حالت، پرسش شما باید درباره رفتار همان بسته، نسخه و تنظیماتش باشد. وجود متدی با نام «ساختاریافته» کافی نیست. اگر مسیر خام در دسترس نیست، از مستندات و آزمون کنترل‌شده روشن کنید تکرار کجا رد می‌شود. بدون این شاهد، نگویید بررسی نام تکراری در برنامه شما انجام شده است.

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

راهنمای قرارداد خروجی ساختاریافته بررسی شکل، معنا و اختیار را جدا می‌کند. موضوع این نوشته یک مرحله زودتر است: آیا همان شیئی که شکلش را بررسی می‌کنیم، بدون انتخاب پنهانی میان دو مقدار ساخته شده است؟

نام را پس از بازکردن نویسه‌های گریز مقایسه کنید

این دو نام در متن متفاوت دیده می‌شوند: "quantity" و "\u0071uantity". پس از بازکردن نویسه گریز، هر دو یک نام‌اند. جست‌وجوی ساده رشته یا شمردن عبارت‌های میان علامت نقل‌قول، جای تجزیه JSON را نمی‌گیرد. همان عبارت ممکن است داخل یک مقدار متنی هم آمده باشد، نه در جای نام فیلد.

RFC 7493 درباره I-JSON نام تکراری را ممنوع می‌کند و برابری را پس از پردازش نویسه‌های گریز می‌سنجد. پذیرش این قاعده برای ابزار، تصمیمی روشن‌تر از اعتماد به پیش‌فرض هر کتابخانه است. البته رعایت همین یک شرط به‌تنهایی به معنی رعایت همه الزامات I-JSON نیست.

دامنه بررسی نیز مهم است. در {"source":{"id":"A"},"target":{"id":"B"}}، دو نام id در دو شیء جدا قرار دارند و تکرار در یک شیء محسوب نمی‌شوند. در عوض، تکرار نام داخل یکی از همین اشیای تو‌در‌تو باید تشخیص داده شود. فهرست سراسری «نام‌های قبلاً دیده‌شده» هر دو حالت را اشتباه می‌گیرد.

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

یک آزمایش کوچک در پایتون، محل کنترل را نشان می‌دهد

مستندات JSON در Python 3.14 می‌گوید رفتار پیش‌فرض، نگه‌داشتن آخرین مقدار نام تکراری است. همان کتابخانه با object_pairs_hook جفت‌های مرتب هر شیء را به تابع شما می‌دهد. این امکان اجازه می‌دهد پیش از ساخت دیکشنری معمولی، تکرار را رد کنید.

کد زیر نمونه آموزشیِ همین کنترل است. ادعای اعتبارسنجی کامل I-JSON، محدودکردن منابع یا ایمن‌سازی همه انواع عدد و نویسه را ندارد:

import json

def unique_object(pairs):
    result = {}
    for name, value in pairs:
        if name in result:
            raise ValueError("duplicate JSON member")
        result[name] = value
    return result

raw = r'{"quantity":2,"\u0071uantity":20}'
assert json.loads(raw)["quantity"] == 20

try:
    json.loads(raw, object_pairs_hook=unique_object)
except ValueError:
    pass
else:
    raise AssertionError("duplicate was accepted")

separate = '{"source":{"id":"A"},"target":{"id":"B"}}'
assert json.loads(separate, object_pairs_hook=unique_object)["target"]["id"] == "B"

پیام خطا عمداً مقدارها یا نام دریافتی را بازنشر نمی‌کند. در برنامه واقعی، شناسه درخواست و محل ساختاریِ پالایش‌شده برای عیب‌یابی مفیدتر از کپی تمام متن حساس در گزارش خطاست. اگر نام فیلد هم از ورودی نامطمئن می‌آید، آن را بی‌قیدوشرط وارد گزارش نکنید.

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

تبدیل به ساختار داخلی هم ممکن است نام‌ها را یکی کند

نام‌های quantity و Quantity از نظر برابری دقیق، دو نام متفاوت‌اند. بااین‌حال، مبدلی که بزرگی و کوچکی حروف را نادیده می‌گیرد ممکن است هر دو را به یک فیلد داخلی وصل کند. بنابراین، رد نام تکراریِ دقیق پایان بررسی نیست؛ قواعد اتصال نام به فیلد را نیز باید بشناسید.

مرجع Go برای encoding/json تفاوت بسته قدیمی v1 با v2 را در پذیرش نام تکراری و تطبیق نام فیلد توضیح می‌دهد. مسیر قدیمی هنگام اتصال به ساختار، تطبیق غیرحساس به بزرگی حروف را هم می‌پذیرد. این رفتار را به همه نسخه‌ها و گزینه‌های Go تعمیم ندهید؛ نسخه و تنظیمات واقعی بخشی از قراردادند.

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

ساختار مقصد را هم برای هر درخواست تازه بسازید. این راهنما رفتار ادغام همه کتابخانه‌ها را یکسان فرض نمی‌کند؛ هدف آن است که آزمون شما نشان دهد مقدار باقی‌مانده از درخواست قبلی، تبدیل نوع یا تابع سفارشی چه اثری بر ورودی اجرا دارد. آزمون باید بعد از آخرین تبدیل، مقدار نهایی را ببیند.

اعتبارسنج، بازبین و ابزار باید یک نسخه را ببینند

به مثال تعداد ۲ و ۲۰ برگردیم. اگر اعتبارسنج شیء یک تجزیه‌گر را ببیند، ولی ابزار دوباره متن اولیه را با تجزیه‌گر دیگری بخواند، بررسی قبلی درباره ورودی واقعی ابزار چیزی ثابت نکرده است. حتی دو بررسی جداگانه دامنه ۱ تا ۵۰ نیز این اختلاف را آشکار نمی‌کنند؛ هر دو مقدار در دامنه‌اند.

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

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

اگر در ادامه تبدیل مجازی رخ می‌دهد، مثل تبدیل واحد تحت قرارداد، ورودی و خروجی آن تبدیل باید قابل‌آزمون باشند. «همان نسخه» به معنی ممنوعیت هر تبدیل نیست؛ یعنی هیچ جزء حق ندارد بی‌قاعده معنای تازه‌ای انتخاب کند و همچنان از تأیید نسخه قبلی استفاده کند.

هش و مرتب‌سازی، ابهامِ حذف‌شده را ترمیم نمی‌کنند

ممکن است تیم پیشنهاد کند متن را مرتب و یکدست کند و سپس هش بگیرد. این کار برای کاربرد مناسب ارزش دارد، ولی ترتیب عملیات را عوض نمی‌کند. اگر دیکشنری قبلاً یکی از مقدارها را کنار گذاشته باشد، یکدست‌سازی بعدی نمی‌تواند ثابت کند متن اولیه تنها یک مقدار داشته است.

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

هش بایت‌های خام نیز سؤال دیگری را پاسخ می‌دهد: آیا دو جزء همان بایت‌ها را دارند؟ داشتن بایت‌های یکسان تضمین نمی‌کند هر دو آن را یکسان تفسیر کنند. در طراحی شواهد، شناسه ورودی خام را از شناسه نمایش پذیرفته‌شده جدا کنید و نسخه قواعد تبدیل را میان آنها ثبت کنید. برای این کار، لازم نیست همه محتوای خام را برای همیشه نگه دارید؛ دسترسی و مدت نگهداری همچنان تابع سیاست داده‌اند.

تکرار را رد کنید؛ مقدار برنده انتخاب نکنید

سیاست پیشنهادی زیر درباره پذیرش ورودی ابزار است، نه همه کاربردهای ممکن JSON. یک نمایشگر بایگانی شاید بخواهد متن معیوب را فقط برای بررسی نشان دهد؛ این اجازه با تبدیل آن به دستور اجرایی فرق دارد.

وضعیت ورودیتصمیم پیشنهادی
یک نام، دو مقدار در همان شیءرد ورودی؛ بدون اجرای ابزار
یک نام با دو نگارش گریزِ معادلهمان رفتار تکرار؛ رد ورودی
دو نام جدا که مبدل به یک فیلد وصل می‌کندرد برخورد در نگاشت یا اصلاح قرارداد مبدل
نام مشابه در دو شیء مستقلاز نظر تکرار مجاز؛ ادامه بررسی شکل و معنا
پاسخ مبهم پس از تبدیل از دسترس خارج شدهثبت محدودیت شواهد؛ ادعای بررسی خام نکنید

از مدل نخواهید صرفاً «JSON را تمیز کند» و سپس خروجی تازه را همان پیشنهاد قبلی بدانید. درخواست دوباره، پیشنهاد تازه‌ای می‌سازد و باید دوباره بررسی شود. اگر کاربر انتخاب مقدار را بر عهده دارد، هر دو مقدار را در یک مسیر بازبینی مجاز و روشن نشان دهید؛ برنامه نباید به جای او آخرین مقدار را انتخاب کند.

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

آزمون را از متن خام تا آرگومان نهایی ادامه دهید

مجموعه کوچک آزمون بسازید: تکرار با مقدارهای متفاوت، تکرار با مقدار برابر، نام گریزدار معادل، تکرار داخل شیء تو‌در‌تو، نام یکسان در دو شیء جدا و برخورد نام مستعار یا بزرگی حروف. دو مورد آخر عمداً یک نتیجه ندارند. کنار آنها یک ورودی سالم نگه دارید تا سخت‌گیری تازه، همه درخواست‌ها را رد نکند.

هر ورودی را از مسیر واقعی میان‌افزار، بسته ارائه‌دهنده، اعتبارسنج و اتصال به ابزار عبور دهید. انتظار فقط «خطا نداد» نباشد: نتیجه پذیرش، آرگومان نهایی و شمار اجرا را ثبت کنید. برای ورودی ردشده، شمار اجرای ابزار باید صفر باشد. برای ورودی سالم، نمایش بازبین و آرگومان اجرا باید مطابق همان قرارداد باشند.

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

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

یادداشت منابع — بازبینی ۹ اکتبر ۲۰۲۶

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

  • RFC 8259؛ قالب JSON — دسامبر ۲۰۱۷؛ بخش ۴ و اختلاف رفتار گیرنده‌ها.
  • RFC 7493؛ I-JSON — مارس ۲۰۱۵؛ بخش ۲٫۳ و نام‌های تکراری پس از بازکردن گریزها.
  • Python 3.14؛ کتابخانه json — مستندات نسخه‌دار با اصلاحات جاری؛ رفتار پیش‌فرض و تابع دریافت جفت‌ها.
  • Go؛ مرجع encoding/json — صفحه بازبینی‌شده با برچسب go1.27.2 و تاریخ ۸ اکتبر ۲۰۲۶؛ تفاوت مسیرهای v1 و v2 و اتصال نام فیلد.
  • RFC 8785؛ JCS — ژوئن ۲۰۲۰، سند Informational؛ پیش‌شرط‌های یکدست‌سازی.
#JSON#خروجی ساختاریافته#یکپارچه‌سازی هوش مصنوعی#اعتبارسنجی ورودی#آزمون نرم‌افزار

مطالب مرتبط

این ابزار را در تیم خودتان راه بیندازید

اگر می‌خواهید ایجنت‌های هوش مصنوعی و اتوماسیون این راهنما را در تیم نرم‌افزار یا فرایندهای سازمانتان به کار بگیرید، از یک آزمایش کوچک شروع کنید که بتوانید نتیجه‌اش را بسنجید.