JEP 540 حالا در مسیر هدف‌گذاری برای JDK 28

JEP 540 با عنوان Simple JSON API (Incubator) از مرحلهٔ Candidate به Proposed و سپس Target برای OpenJDK منتقل شده و در فهرست هدف‌های JDK 28 قرار گرفته است. در صورت تصویب و عرضه، یک API جمع‌وجور و درون-JDK برای پردازش اسناد JSON بر اساس RFC 8259، بدون نیاز به وابستگی خارجی، در ماژول آزمایشی jdk.incubator.json فراهم خواهد شد.

هدف و محدوده طراحی

JEP 540 برای نیازهای سبک و رایج طراحی شده است؛ مثال‌ها شامل خواندن فایل‌های پیکربندی، بررسی پاسخ‌های REST و تولید payloadهای کوچک JSON است. طراحی به‌طور آگاهانه از قابلیت‌های پیشرفتهٔ کتابخانه‌هایی مثل Jackson و Gson فاصله می‌گیرد و چند حوزهٔ شبه‌پیشرفته را شامل نمی‌شود.

  • موارد هدف: خواندن و نوشتن اسناد کوچک، پیمایش درختی، استفاده در ابزارها و اسکریپت‌های ساده.
  • موارد خارج از محدوده: نگاشت خودکار داده (data binding)، استریمینگ پیشرفته، اعتبارسنجی schema و سفارشی‌سازی عمیق.

هستهٔ API: Json و JsonValue

طراحی حول کلاس Json و رابط بسته‌شده (sealed) JsonValue شکل گرفته است. JsonValue شش زیررابط غیر-sealed برای نمایش شیء، آرایه، رشته، عدد، بولین و null تعریف می‌کند. نمونه‌های بازگشتی غیرقابل‌تغییر و thread-safe هستند.

پارس کردن و پیمایش

پارس کردن یک سند کامل در حافظه با روش‌هایی مانند Json.parse(String) یا Json.parse(char[]) یک JsonValue برمی‌گرداند. نمونهٔ زیر نحوهٔ خواندن مقدار دما را نشان می‌دهد:

String body = ...; // JSON response body
int temperature = Json.parse(body)
    .get("properties")
    .get("periods")
    .get(0)
    .get("temperature")
    .asInt();

روش طراحی این است که متدهای دسترسی مستقیماً روی JsonValue اعلان شوند تا تبدیل‌های مکرر لازم نباشد. فراخوانی get(String) روی مقداری غیر-شیء یا get(int) روی مقداری غیر-آرایه، درخواست عضو غایب یا استفاده از اندیس نامعتبر منجر به پرتاب JsonValueException خواهد شد.

نمونه تصویری API

نمایی از ساختار و استفاده از Simple JSON API در جاوا

ساخت سند JSON: کارخانه‌های صریح

مقادیر JSON از طریق متدهای کارخانه‌ای ساخته می‌شوند، برای مثال:

JsonObject document = JsonObject.of(Map.of(
    "service", JsonString.of("web_server"),
    "id", JsonNumber.of(3),
    "active", JsonBoolean.of(true)
));

فراخوانی toString() روی یک JsonValue خروجی JSON فشرده تولید می‌کند و Json.toDisplayString(...) نسخهٔ قالب‌بندی‌شده برای نمایش فراهم می‌آورد. کارخانه‌های صریح وضوح نوع JSON را افزایش می‌دهند اما نیازمند بسته‌بندی صریح رشته‌ها، اعداد و بولین‌ها پیش از افزودن به اشیاء یا آرایه‌ها هستند؛ این نکته در دورهٔ آزمایشی احتمالاً بازخورد دریافت خواهد کرد.

سیاست سخت‌گیرانهٔ پارسینگ

پارسر JEP 540 حالت ملایم ندارد: کامنت‌ها، کاماهای پایانی و گسترش‌های نحو مشابه پذیرفته نمی‌شوند. این پیاده‌سازی تکرار نام اعضای شیء را خطا می‌داند، در حالی که RFC 8259 صرفاً یکتایی نام‌ها را توصیه می‌کند. رفتارهای متفاوت پارسرها نسبت به نام‌های تکراری می‌تواند ریسک سازگاری بین‌عملکردی ایجاد کند؛ JEP 540 تصمیم گرفته تکرار را ممنوع کند.

نحو نامعتبر یا نام‌های تکراری باعث پرتاب unchecked JsonParseException می‌شوند که موقعیت صفر-محور خط و ستون رخداد را ثبت می‌کند؛ این استثنا ساختار کامل سند JSON را افشا نمی‌کند.

یکپارچگی با pattern matching و تبدیل‌ها

از آنجا که JsonValue در یک سلسله‌مراتب بسته‌شده تعریف شده، استفاده از pattern matching برای تشخیص نوع‌ها (مثلاً switch بر مبنای نوع) ساده و طبیعی است. نمونه:

long id = switch (json.get("id")) {
  case JsonNumber number -> number.asLong();
  case JsonString string -> Long.parseLong(string.asString());
  default -> throw new JsonValueException("Unexpected id type");
};

روش نام‌گذاری متدهای تبدیل با پیشوند as... دنبال می‌شود. asInt() و asLong() مقدار دقیق صحیح در محدودهٔ مقصد را می‌طلبند، asDouble() مقدار را به double متناهی تبدیل می‌کند اما ممکن است دقت از دست برود. asBoolean()، asMap() و asList() نماهای جاوایی برای بولین‌ها، اشیاء و آرایه‌ها فراهم می‌کنند؛ این نماها تغییرناپذیرند و شامل نمونه‌های JsonValue هستند، نه مقادیر اولیهٔ جاوا.

کنترل وجود عضو و تفاوت با null

برای اعضای اختیاری، tryGet(String) یک Optional<JsonValue> برمی‌گرداند که وقتی عضو غایب باشد خالی است. فراخوانی این متد روی مقداری غیر-شیء همچنان JsonValueException را پرتاب می‌کند. API میان عضو غایب و عضوی که مقدار JSON null دارد تمایز قائل می‌شود: tryGet() در صورت وجود عضو با مقدار JSON null یک Optional حاوی JsonNull بازمی‌گرداند، در حالی که tryValue() برای همان مورد Optional خالی برمی‌گرداند.

پیشینه و تفاوت با JEP 198

پیشنهاد قبلی JEP 198 (Light-Weight JSON API) در سال 2014 مطرح شد اما وارد JDK نشد. طراحی کنونی JEP 540 بر سلسله‌مراتب مقدار درون‌-حافظه متمرکز است و نگاشت اشیاء، استریمینگ و اعتبارسنجی schema را به ابزارها و کتابخانه‌های موجود واگذار می‌کند.

مسیر پیش‌روی توسعه‌دهندگان

در صورت ورود JEP 540 به JDK، برنامه‌هایی که روی class-path اجرا می‌شوند باید ماژول آزمایشی را با گزینهٔ --add-modules jdk.incubator.json فعال کنند. دورهٔ آزمایشی فرصت مناسبی است تا جامعهٔ OpenJDK مدل پیمایش، معناشناسی استثناها، رفتار تبدیل‌های عددی و آسایش ساخت اسناد را ارزیابی کرده و بازخورد فنی ارائه دهد.

چشم‌انداز

JEP 540 مسیر دستیابی جاوا به یک API رسمی و سبک برای وظایف متداول با JSON را هموار می‌کند. جزئیات اجرایی—از جمله روش‌های ساخت سند و سیاست‌های خطا—در دورهٔ آزمایشی تعیین خواهند شد؛ توسعه‌دهندگان باید این فرایند را دنبال کنند و بازخورد ارائه دهند تا API در صورت تثبیت با نیازهای اکوسیستم هماهنگ شود.