JSON Toolkit

JSON Schema পুনরায় ব্যবহার: $ref, $defs ও allOf/oneOf গঠন

সংক্ষেপে: $ref পুনর্ব্যবহার সমাধান করে, allOf ও oneOf গঠন সমাধান করে, আর দুইটি একসঙ্গে স্তূপ করা হলেই ভাঙে। $id, $defs, শর্তাধীন, পুনরাবৃত্তি, বান্ডলিং ও ডিবাগিং।

মধ্যবর্তী~11 মিনিট পড়াপ্রকাশ 2026-10-31আপডেট 2026-10-31
JSON Schema-এ $ref, $defs ও allOf/oneOf দিয়ে পুনর্ব্যবহার ও গঠনের চিত্র

মূল কথা: আগে ভাগ করা কাঠামো $defs-এ বের করে $ref দিয়ে রেফারেন্স করুন, তারপর allOf/oneOf-এর দিকে হাত বাড়ান; শাখা একসঙ্গে মিলতে পারলে anyOf বা const ট্যাগ ব্যবহার করুন, আর oneOf কেবল সত্যিই ঠিক একটি মিল দরকার এমন ক্ষেত্রে রাখুন।

কপি-পেস্ট দশ মিনিট বাঁচায় আর তিন মাস পরে একটি নীরব ফোর্কের দাম নেয়, যখন কেউ মনে রাখে না কোন কপি সঠিক।

১. পুনর্ব্যবহারের তিন স্তর — ফাইল ভাগ করার আগে একটি বাছুন

ফিল্ড বাড়তে শুরু করলে প্রথম প্রবণতা হলো সব বাধ্যবাধকতা এক ফাইলে ঢোকা। এটি প্রকাশের দ্রুততম পথ, কিন্তু কয়েকশো লাইনের পরে প্রতিটি ফিল্ড পরিবর্তন তিন জায়গায় মেলাতে হয়। একটি বাদ গেলে "লোকালি পাস, প্রোডাকশনে ৫০০"। পুনর্ব্যবহার মানে একটি তথ্য একবার লেখা আর সব জায়গায় রেফারেন্স করা।

JSON Schema তিন স্তর দেয়, প্রতিটির পরিধি আলাদা:

স্তর বাহক পরিধি সাধারণ ব্যবহার
উপ-কাঠামো $defs + $ref একটি স্কিমা ফাইল ঠিকানা, টাকা, পেজিনেশন, এরর আকৃতি
ফাইল স্বতন্ত্র স্কিমা + $id একটি সার্ভিসের কয়েকটি ফাইল ব্যবহারকারী, অর্ডার, পণ্য সত্তা
চুক্তি লাইব্রেরি স্কিমা রিপোজিটরি + বান্ডল করা আর্টিফ্যাক্ট একাধিক সার্ভিস, একাধিক ভাষা OpenAPI কম্পোনেন্ট, প্রকাশিত চুক্তি

বেশিরভাগ প্রকল্পে প্রথম স্তরই যথেষ্ট। ফাইলে ভাগ করা তবেই লাভজনক যখন দুই সার্ভিস সত্যিই একই চুক্তি ভাগ করে, বা স্কিমা অন্য ভাষা ভোগ করে এমন প্রকাশিত আর্টিফ্যাক্ট। খুব আগে ভাগ করলে $id রেজোলিউশন ও আপেক্ষিক পাথ ডিবাগ করা কঠিন হয়, আর সেই খরচ সাধারণত ডুপ্লিকেশন এড়িয়ে বাঁচানো সময়ের চেয়ে বেশি।

একটি সরল হাতের নিয়ম: একটি কাঠামো দুই জায়গায় এলে এক কপি সহনীয়; তৃতীয় জায়গায় এলে $defs-এ উন্নীত করুন; কেবল যখন অন্য ভাষা বা রিপোজিটরিকে তা পড়তে হবে তখনই নিজস্ব $id-সহ স্বতন্ত্র ফাইলের যোগ্য।

২. $id পরিচয় তৈরি করে, $ref লাফ দেয়

$id মন্তব্য নয়। এটি স্কিমার বেস URI ঘোষণা করে। $ref মান একটি URI রেফারেন্স: বর্তমান বেস URI-এর বিরুদ্ধে রেজলভ হয়ে পরম URI হয়, তারপর #-এর পরের ফ্র্যাগমেন্ট একটি নোড লোকেট করে। বেস URI ভুল হলে প্রতিটি আপেক্ষিক রেফারেন্স অন্য জায়গায় ইশারা করে, অথচ এরর সাধারণত "can't resolve reference" বলে, ভুল হোস্টের নাম দেয় না।

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://bbdrug.com/schemas/order.json",
  "$defs": {
    "address": {
      "$anchor": "address",
      "type": "object",
      "required": ["city"],
      "properties": {
        "city": { "type": "string", "minLength": 1 },
        "detail": { "type": "string" }
      }
    }
  },
  "type": "object",
  "required": ["shipTo"],
  "properties": {
    "shipTo": { "$ref": "#/$defs/address" },
    "billTo": { "$ref": "#address" }
  }
}

shipTo JSON Pointer ব্যবহার করে আর billTo অ্যাঙ্কর; দুটোই একই নোডে পড়ে। নিচের টেবিল প্রতিটি রূপের রেজোলিউশন দেখায়:

রূপ কোথায় ইশারা করে Draft নোট
# চলতি স্কিমার রুট সব পুনরাবৃত্তির জন্য সাধারণ
#/$defs/address $defs-এর অধীনের নোড 2019-09 ও পরে সবচেয়ে স্পষ্ট; পছন্দনীয়
#address নামযুক্ত অ্যাঙ্কর 2020-12-তে $anchor draft-07 লিখত $id: "#address"
common.json বেস URI-র আপেক্ষিক ফাইল সব রেজিস্টার বা বান্ডল করতে হবে
https://example.com/x.json পরম URI সব প্রোডাকশনে রানটাইমে ফেচ এড়ান

একটি সূক্ষ্ম ফাঁদ: $defs-এর ভেতরের উপ-স্কিমা নিজস্ব $id ঘোষণা করলে তা নিচের সবকিছুর নতুন বেস URI হয়। অন্য ফাইল থেকে কপি করা স্কিমা পেস্ট করলে রয়ে যাওয়া $id তার আপেক্ষিক রেফারেন্স মূল হোস্টে ঘুরিয়ে দেয় — আর ভ্যালিডেটর কেবল সত্যিই রেজলভ করতে গেলেই খেয়াল করে।

৩. $defs কন্টেইনার, ভ্যালিডেশন সাইট নয়

$defs একটি সাধারণ ম্যাপ। কোনো $ref সেখানে ইশারা না করা পর্যন্ত ভেতরের কিছুই কার্যকর হয় না। $defs-এর উপরে বসানো required তালিকা, যেখানে কোনো রেফারেন্স আসে না, কিছুই যাচাই করে না, যত কঠোরই দেখাক। এটি স্পেসিফিকেশন যেমন উদ্দেশ্য করেছিল তেমন, ভ্যালিডেটর বাগ নয়।

{
  "$defs": {
    "money": {
      "type": "string",
      "pattern": "^[0-9]+\\.[0-9]{2}$",
      "description": "A two-decimal money string that avoids floating-point drift"
    },
    "page": { "type": "integer", "minimum": 1 }
  },
  "properties": {
    "total": { "$ref": "#/$defs/money" },
    "page": { "$ref": "#/$defs/page" }
  }
}

কয়েকটি প্রথা দলে ঠিক করা মূল্যবান:

  • $defs-এর ভেতরের কি-এর কোনো সেমান্টিক্স নেই। address, Address ও addr — সবই বৈধ, তাই একটি শৈলীতে একমত হন।
  • 2019-09-এর আগে কি-ওয়ার্ড ছিল definitions। দুটোই $ref দিয়ে লক্ষ্য করা যায়, কিন্তু strict মোড definitions-কে অজানা কি-ওয়ার্ড রিপোর্ট করতে পারে।
  • $defs যেকোনো স্কিমা রাখতে পারে, allOf খণ্ড বা নিজস্ব $id-যুক্ত স্বতন্ত্র উপ-স্কিমাসহ।
  • $defs-কে টেমপ্লেট উত্তরাধিকার ধরবেন না। এটি কেবল সংজ্ঞা রাখে; অর্থ তবু রেফারেন্সকারী প্রসঙ্গ থেকে আসে।

৪. allOf, anyOf ও oneOf বিনিময়যোগ্য নয়

তিনটিই একাধিক শাখা জড়ায়, কিন্তু গ্রহণযোগ্যতার নিয়ম আলাদা। ডেটা দুই শাখা মিললে allOf ও anyOf পাস করে, অথচ oneOf ব্যর্থ — "স্কিমা ভাঙা" সিদ্ধান্তে পৌঁছানোর সবচেয়ে সাধারণ কারণ।

কি-ওয়ার্ড সর্বনিম্ন মিল সর্বোচ্চ মিল দুই শাখা মেলা ডেটা এরর গুণমান সাধারণ ব্যবহার
allOf সব অসীম পাস ভালো, একবারে একটি বাধ্যবাধকতা বাধ্যবাধকতা খণ্ড মেলানো
anyOf ১ অসীম পাস দুর্বল, শাখা স্তূপ হয় "ইমেইল বা ফোন, যেকোনো একটি"
oneOf ১ ১ ব্যর্থ দুর্বল discriminatorযুক্ত union
not ০ ০ ব্যর্থ সবচেয়ে খারাপ কিছু মান বাদ দেওয়া; কম ব্যবহৃত
{
  "allOf": [
    { "$ref": "#/$defs/auditable" },
    { "$ref": "#/$defs/softDeletable" },
    { "required": ["email"] }
  ],
  "unevaluatedProperties": false
}

allOf অবজেক্ট সংজ্ঞা মার্জ করে না; কেবল প্রতিটি শাখা সত্য হতে বলে। প্রতিটি শাখা স্বাধীনভাবে মূল্যায়িত হয়, তাই এক শাখার ভেতরের additionalProperties: false কেবল সেই শাখার ঘোষিত কি জানে আর অন্য শাখার ঘোষিত ফিল্ডকে বাড়তি বলে চিহ্নিত করে। 2020-12-তে সমাধান হলো অজানা-প্রপার্টি নীতি টপ লেভেলে রেখে unevaluatedProperties: false ব্যবহার করা, যা সব শাখার পরে একবার মূল্যায়িত হয়।

anyOf "কয়েকটি বৈধ আকৃতি"-য় ব্যবহার করুন, "কয়েকটি পরস্পর-একচেটিয়া আকৃতি"-য় নয়। দুই শাখা একসঙ্গে মিলতে পারলে oneOf সেই কুখ্যাত অকেজো must match exactly one schema in oneOf দেয়।

৫. if/then/else ও discriminator: শর্তকে সিদ্ধান্তযোগ্য করুন

শর্তাধীন বাধ্যবাধকতা সাধারণত oneOf-এর চেয়ে ভালো এরর দেয় কারণ সিদ্ধান্তের পথ স্পষ্ট: if পাস করলে then প্রযোজ্য; নাহলে else প্রযোজ্য।

{
  "type": "object",
  "required": ["kind"],
  "properties": { "kind": { "enum": ["text", "image"] } },
  "if": {
    "properties": { "kind": { "const": "image" } },
    "required": ["kind"]
  },
  "then": { "required": ["url", "width", "height"] },
  "else": { "required": ["content"] }
}

তিনটি বিবরণ ঠিক করে এটি উদ্দেশ্য অনুযায়ী আচরণ করবে কি না:

  1. if-এর ভেতরের properties এন্ট্রি কি অনুপস্থিত হলে ব্যর্থ হয় না। "kind আছে ও image-এর সমান" প্রকাশ করতে required-এর সঙ্গে জোড়া দিন।
  2. if নিজে কখনো ভ্যালিডেশন এরর উৎপন্ন করে না। এটি ব্যর্থ হলে কেবল else-এ রুট করে, তাই নিজে থেকে খারাপ ডেটা আটকাতে পারে না।
  3. একা then বা else নিষ্ক্রিয়। এরা কেবল if-এর পাশে প্রযোজ্য।

Union শাখা বাড়লে শর্তাধীন শৃঙ্খল অসামলনযোগ্য নেস্টে পরিণত হয়। আরও মজবুত প্যাটার্ন হলো প্রতিটি শাখার ভেতরে একটি ট্যাগ ফিল্ড লক করা যাতে শাখা গঠনগতভাবেই পরস্পর-একচেটিয়া হয়:

{
  "oneOf": [
    {
      "properties": { "kind": { "const": "card" } },
      "required": ["kind", "cardNumber"]
    },
    {
      "properties": { "kind": { "const": "account" } },
      "required": ["kind", "accountId"]
    }
  ]
}

discriminator OpenAPI 3.x এক্সটেনশন, JSON Schema কি-ওয়ার্ড নয়। Ajv এটি সমর্থন করে যখন discriminator: true অপশন চালু, কিন্তু এর ম্যাপিং থাকে mapping-এ আর টুলচেইন জুড়ে পোর্টেবল নয়। স্কিমা কয়েকটি ভ্যালিডেটরে চলতে হলে const ট্যাগ প্যাটার্নকে অগ্রাধিকার দিন, যা কোনো এক্সটেনশনের উপর নির্ভর করে না।

৬. পুনরাবৃত্ত রেফারেন্স: ট্রি, লিংকড লিস্ট ও পরস্পর-উদ্ধৃত ফাইল

পুনরাবৃত্ত কাঠামোর বিশেষ সিনট্যাক্স দরকার নেই — নিজের দিকে একটি $ref-ই যথেষ্ট। ভ্যালিডেটর কম্পাইল সময়ে রেফারেন্স অসীমভাবে প্রসারিত করে না; ডেটার প্রকৃত গভীরতা অনুযায়ী পুনরাবৃত্তি করে, তাই কয়েক ডজন লাইন যেকোনো উচ্চতার ট্রি বর্ণনা করে।

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://bbdrug.com/schemas/tree-node.json",
  "$defs": {
    "node": {
      "type": "object",
      "required": ["name"],
      "properties": {
        "name": { "type": "string", "minLength": 1 },
        "children": {
          "type": "array",
          "items": { "$ref": "#/$defs/node" }
        }
      }
    }
  },
  "$ref": "#/$defs/node"
}

তিনটি সীমানা আগেই ঠিক করা মূল্যবান:

  • ডেটার গভীরতা: স্কিমা কোনো সীমা চাপায় না, কিন্তু ভ্যালিডেশন ও সিরিয়ালাইজেশন — দুটোই গভীরতার সমানুপাতিক স্ট্যাক খায়। পাবলিক API-তে ব্যবসায়িক সীমা যোগ করুন, ধরা যাক আট স্তর, নাহলে দূষিতভাবে গভীর পেললোড স্ট্যাক ওভারফ্লো করতে পারে।
  • পারস্পরিক রেফারেন্স: A B-কে আর B A-কে রেফারেন্স করা বৈধ, কিন্তু দুই স্কিমাই আগে রেজিস্টার করতে হবে; নাহলে রেজোলিউশন ক্রম ফল ঠিক করে।
  • Nullable শাখা: পুনরাবৃত্ত ফিল্ড null হতে পারলে "type": ["object", "null"] লিখুন বা oneOf-এ মুড়ুন। $ref nullability উত্তরাধিকার পায় না।

৭. ফাইল-ছাড়ানো রেফারেন্স ও বান্ডলিং: প্রোডাকশনে কখনো স্কিমা ফেচ করবেন না

ফাইল-ছাড়ানো রেফারেন্স ডেভেলপমেন্টে প্রায়ই "কাজ করে" কারণ ভ্যালিডেটর নেটওয়ার্কে $id দিয়ে লক্ষ্য ফেচ করে। ডিপ্লয় সময়ে নেটওয়ার্ক নীতি বদলালে পুরো ভ্যালিডেশন শৃঙ্খল ভাঙে। প্রোডাকশন নিয়ম সরল: প্রতিটি রেফারেন্সড স্কিমা ইন-প্রসেসে রেজিস্টার বা আগেই বান্ডল করতে হবে।

import Ajv2020 from 'ajv/dist/2020';
import addressSchema from './address.json' with { type: 'json' };
import orderSchema from './order.json' with { type: 'json' };

const ajv = new Ajv2020({ allErrors: true, strict: true });

// Register the referenced file first; the key must equal its $id, or relative refs won't resolve
ajv.addSchema(addressSchema, 'https://bbdrug.com/schemas/address.json');

// Compile the entry schema once, at module scope
export const validateOrder = ajv.compile(orderSchema);

order.json-এ $id: "https://bbdrug.com/schemas/order.json" থাকলে তার "$ref": "address.json" রেজলভ হয়ে https://bbdrug.com/schemas/address.json, যা রেজিস্টার্ড কি-এর সঙ্গে মেলে। একটি অক্ষর আলাদা হলেই can't resolve reference।

তিন ব্যবহারিক পথের তুলনা:

পথ কীভাবে শক্তি খরচ
রানটাইমে রেজিস্টার প্রতি ফাইলে addSchema বিল্ড ধাপ নেই, ফাইল স্বতন্ত্র থাকে রেজিস্ট্রেশন ক্রম ও কি ম্যানুয়াল
বিল্ড সময়ে বান্ডল ref পার্সার দিয়ে এক ফাইলে মার্জ রানটাইম নির্ভরতা শূন্য বড় আর্টিফ্যাক্ট, শব্দযুক্ত ডিফ
অ্যাসিঙ্ক লোড loadSchema + compileAsync ঘন বদলানো স্কিমা সামলায় নেটওয়ার্ক ও ক্যাশে চিন্তা; ক্রিটিক্যাল পাথে এড়ান

ফ্রন্টএন্ড প্রকল্পে বিল্ড সময়ে বান্ডলিং নিরাপদ। সার্ভারে রেজিস্ট্রেশনই যথেষ্ট, শর্ত হলো একটি টেস্ট প্রতিটি রেফারেন্স রেজলভ হয় প্রমাণ করবে। যেভাবেই হোক, CI-তে compile চালান যাতে রেফারেন্স এরর প্রথম প্রোডাকশন রিকোয়েস্টের বদলে মার্জের আগেই প্রকাশ পায়।

৮. ডিবাগিং ক্রম: রেজলভ, কম্পাইল, তারপর শাখা দেখুন

প্রায় প্রতিটি $ref সমস্যা তিন ধাপে লোকেট করা যায়, আর ক্রম গুরুত্বপূর্ণ:

লক্ষণ সম্ভাব্য কারণ সমাধান
can't resolve reference ... from id ... $id-তে টাইপো, ভুল আপেক্ষিক বেস, লক্ষ্য রেজিস্টার্ড নয় $id ও রেজলভ হওয়া পরম URI ছাপুন, তারপর রেজিস্টার্ড কি-এর সঙ্গে তুলনা করুন
schema with key or id already exists একই $id দুইবার রেজিস্টার্ড, বা compile বারবার কল রেজিস্ট্রেশন মডিউল ইনিটে সরিয়ে একবার চালান
strict mode: unknown keyword: "definitions" draft-07 সিনট্যাক্স মেশানো $defs-এ নাম বদলান; strict মোড বন্ধ করবেন না
ডেটা দুই oneOf শাখা মেলে শাখা বাধ্যবাধকতা ওভারল্যাপ, ট্যাগ ফিল্ড নেই প্রতিটি শাখায় const যোগ করুন, বা anyOf-তে যান
$defs-এর ভেতরের বাধ্যবাধকতার কোনো প্রভাব নেই কিছুই সেগুলো রেফারেন্স করে না রেফারেন্স পাথ দেখুন; $defs নিজে কিছু যাচাই করে না

ডিবাগ করার সময় message-এর আগে schemaPath পড়ুন: এটি ব্যর্থ শাখা ও কি-ওয়ার্ডের নাম দেয়, ইংরেজি গদ্যের চেয়ে অনেক বেশি নির্ভরযোগ্য। কম্পাইল করা ভ্যালিডেটর প্রতি রিকোয়েস্টে আবার কম্পাইল না করে মডিউল স্কোপে ক্যাশ করুন। প্রতিটি স্কিমা পরিবর্তনে একটি পাসিং ও একটি ব্যর্থ কেস — দুটোই ঢাকুন; কেবল হ্যাপি পাথ পরীক্ষা করা কিছুই পরীক্ষা করে না। ভ্যালিডেটর বাছাই ও এরর-মেসেজ নকশার জন্য দেখুন JSON Schema: মূল বিষয় থেকে প্রোডাকশন; স্কিমাকে টাইপ জেনারেশনে জোড়ানোর জন্য দেখুন স্কিমা ও TypeScript টাইপের দুইমুখী সিঙ্ক।

৯. সাধারণ প্রশ্ন

$ref কি type বা description-এর পাশে থাকতে পারে?

2020-12 $ref-কে পাশের কি-ওয়ার্ডের সঙ্গে অনুমোদন করে, আর সেই পাশেরগুলো স্বাভাবিকভাবে মূল্যায়িত হয়। draft-07-তে পাশেরগুলো উপেক্ষিত আর কেবল $ref প্রযোজ্য। একই স্কিমা draft জুড়ে ব্যবহার করলে এটাই সবচেয়ে বড় আচরণগত পার্থক্য, তাই মাইগ্রেশনের আগে নিশ্চিত করুন আপনার ভ্যালিডেটর কোন draft সমর্থন করে।

$defs নাকি definitions ব্যবহার করব?

নতুন কাজে $defs; এটি 2019-09-এর সঙ্গে এসেছে। definitions 2020-12-তে কি-ওয়ার্ড নয়। $ref: "#/definitions/foo"-এর মতো পয়েন্টার তবু রেজলভ হতে পারে কারণ এটি সাধারণ JSON Pointer, কিন্তু strict মোড অজানা কি-ওয়ার্ড রিপোর্ট করবে। দুই শৈলী মিশিয়ে রাখলে একটি ল্যান্ডমাইন থাকে যা কেবল CI-তে ফাটে।

oneOf বৈধ-দেখতে ডেটা প্রত্যাখ্যান করে কেন?

কারণ ডেটা একসঙ্গে দুই শাখা পূরণ করে। oneOf ঠিক একটি দাবি করে, আর যে শাখা কেবল অবজেক্টের অংশ বাধ্যতামূলক করে তা সন্তুষ্ট হয় এমনকি অন্য শাখার ফিল্ড অনুপস্থিত থাকলেও। তাদের পরস্পর-একচেটিয়া করতে প্রতিটি শাখায় const ট্যাগ যোগ করুন।

পুনরাবৃত্ত রেফারেন্স কি চিরকাল লুপ করবে?

না। ভ্যালিডেটর কম্পাইল সময়ে রেফারেন্স প্রসারিত করে না; প্রকৃত ডেটার উপর পুনরাবৃত্তি করে। আসল ঝুঁকি স্ট্যাক ওভারফ্লো করার মতো গভীর ডেটা আর পরস্পর-রেফারেন্সিং ফাইলের ভুল রেজোলিউশন ক্রম। পুনরাবৃত্ত ফিল্ডে ব্যবসায়িক-স্তরের গভীরতা সীমা যেকোনো স্কিমা কৌশলের চেয়ে বেশি কার্যকর।

প্রোডাকশন কি রিমোট $ref-এর উপর নির্ভর করতে পারে?

না। রিমোট রেফারেন্স ভ্যালিডেশনের প্রাপ্যতাকে নেটওয়ার্কের সঙ্গে বাঁধে: ডোমেইন বদল, মেয়াদোত্তীর্ণ সার্টিফিকেট, অভ্যন্তরীণ নেটওয়ার্ক বিচ্ছিন্নতা — সবই শৃঙ্খল ভাঙে। পূর্বানুমেয় আচরণ চাইলে রেফারেন্সড স্কিমা আর্টিফ্যাক্টে বান্ডল করুন বা স্টার্টআপে রেজিস্টার করুন।

$defs ও আলাদা ফাইলের মধ্যে কীভাবে ঠিক করব?

দুই জায়গায় আসা কাঠামো একবার কপি করা যায়; তৃতীয় জায়গায় এলে $defs-এ উন্নীত করুন। কেবল যখন অন্য ভাষা বা রিপোজিটরিকে পড়তে হবে তখনই নিজস্ব $id-সহ স্বতন্ত্র ফাইলের যোগ্য।

$defs-এর ভেতরের বাধ্যবাধকতার কোনো প্রভাব নেই কেন?

$defs কেবল কন্টেইনার, আর কোনো $ref যেসব নোডে ইশারা করে না তারা ভ্যালিডেশনে অংশ নেয় না। $defs-এর উপরে বসানো required তালিকা, যেখানে কোনো রেফারেন্স আসে না, তবু ডেটা পাস করতে দেয় — এটাই স্পেসিফিকেশন, ভ্যালিডেটর বাগ নয়।

allOf শাখার ভেতরে additionalProperties: false রাখা উচিত?

না। প্রতিটি শাখা স্বাধীনভাবে মূল্যায়িত হয়, তাই এক শাখা অন্য শাখার ঘোষিত ফিল্ডকে বাড়তি ধরে। 2020-12 পদ্ধতি হলো অজানা-ফিল্ড নীতি টপ লেভেলে সরিয়ে unevaluatedProperties: false ব্যবহার করা, যা সব শাখার পরে একবার মূল্যায়িত হয়।