الفكرة في سطر
الـ JSON Schema هي JSON document بيوصف شكل JSON تاني: نوعه إيه، فيه حقول إيه، إيه الإجباري، وإيه الحدود.
- الـ schema: الوصف نفسه، يعني “الفورم” اللي فيه الشروط.
- الـ instance: الـ JSON اللي عايز تراجعه، يعني “الورقة اللي اليوزر ملاها”.
- الـ validator: المكتبة اللي بتاخد الاتنين وترد بـ valid أو invalid ومعاها قايمة errors. في Node أشهرها
ajv. - الـ specification: الـ standard نفسه منشور على
json-schema.org، وليه إصدارات اسمها drafts (فصل ٧).
أول schema
user.schema.json
{ "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "https://example.com/schemas/user.json", "title": "User", "type": "object", "properties": { "id": { "type": "integer", "minimum": 1 }, "name": { "type": "string", "minLength": 2, "maxLength": 50 }, "email": { "type": "string", "format": "email" }, "role": { "enum": ["admin", "user", "guest"], "default": "user" } }, "required": ["id", "name", "email"], "additionalProperties": false}اقراها كأنها جملة عادية:
- الـ
type: "object": الـ JSON لازم يبقى object، مش array ولا string. - الـ
properties: لو فيهidلازم يبقى integer من 1 وطالع، ولو فيهnameلازم string من 2 لـ 50 حرف. - الـ
required: الـidوالـnameوالـemailلازم يبقوا موجودين. - الـ
additionalProperties: false: ممنوع أي حقل مش مذكور فيproperties. - الـ
$schema: بيقول الـ schema دي مكتوبة بأنهي draft. - الـ
$id: اسم فريد للـ schema (URI)، بنستخدمه في الـ$refبعدين (فصل ٦).
نشغّلها بـ Ajv
first.js
import Ajv from "ajv/dist/2020.js";import addFormats from "ajv-formats";import { readFileSync } from "node:fs";const schema = JSON.parse(readFileSync("./user.schema.json", "utf8"));const ajv = new Ajv({ allErrors: true });addFormats(ajv); // من غيرها Ajv مش هيعرف "email"const validate = ajv.compile(schema); // بيحوّل الـ schema لـ functionconst bad = { id: "1", name: "S", email: "not-an-email", role: "boss", isAdmin: true };if (!validate(bad)) { for (const e of validate.errors) console.log(e.instancePath || "(root)", e.message);}الـ output الحقيقي:
output
(root) must NOT have additional properties/id must be integer/name must NOT have fewer than 2 characters/email must match format "email"/role must be equal to one of the allowed values- الـ
instancePath: مكان الغلط في الـ JSON بصيغة JSON Pointer، زي/items/0/qty. - الـ
message: الرسالة بالإنجليزي، وفيهkeywordوparamsلو عايز تبني رسالتك بنفسك (فصل ٨). - الـ
allErrors: true: خلّاه يجمع كل الأخطاء. من غيره بيقف عند أول غلط.
الـ keywords نوعين
كل حاجة جوه الـ schema اسمها keyword. والـ keywords نوعين:
Assertions: بتقرر
بتقول valid ولا لأ: type، required، minLength، pattern، enum، maximum. لو الشرط اتكسر، الـ validation بتفشل.
Annotations: بتوصف
معلومات بس، مش بتفشّل حاجة: title، description، default، examples، deprecated، readOnly، writeOnly، $comment. بتستخدمها الأدوات زي الـ docs والـ editors والـ form generators.
- مثال مهم: الـ
defaultannotation، يعني Ajv مش هيحطه في البيانات إلا لو فعّلتuseDefaults(فصل ٨). - والـ
format: في2020-12هو annotation بالـ standard، بس Ajv بيعامله كـ assertion لو ضفتajv-formats(فصل ٤).
حاجات مش واخد بالك منها
- الـ schema الفاضية
{}: بتقبل أي JSON. كل keyword بيضيّق، ومن غير keywords مفيش قيود. - الـ
trueوfalse: ينفعوا يبقوا schema كاملة.trueزي{}بتقبل أي حاجة، وfalseبترفض أي حاجة. - الـ keyword بيطبق على نوعه بس:
minLengthعلى number مش بيعمل حاجة، وpropertiesعلى string مش بتعمل حاجة. عشان كده دايمًا اكتبtype. - الـ
propertiesلوحدها مش بتخلي الحقل إجباري: لازمrequired. - الـ keywords غير المعروفة: الـ standard بيقول تتجاهل. بس Ajv في الـ
strictmode (الـ default) بيرمي error، وده بيلقط الـ typos:
typo.js
new Ajv().compile({ type: "string", minLenght: 3 });// Error: strict mode: unknown keyword: "minLenght"الـ vocabularies
من 2019-09 الـ spec اتقسمت لمجموعات keywords اسمها vocabularies: core (زي $id و$ref)، وapplicator (زي properties وallOf)، وvalidation (زي type وminimum)، وunevaluated، وformat-annotation، وcontent، وmeta-data.
- الفايدة: أداة تقدر تقول “أنا بدعم الـ vocabularies دي بس”، وحد يقدر يعمل vocabulary خاص بيه.
- في الشغل اليومي: غالبًا مش هتحتاج تفكر فيها. افتكر بس إن
formatليه vocabulary annotation وvocabulary assertion منفصلين.
خلّصت الفصل لما
🧠 اختبر نفسك
١) schema فيها properties بـ name وemail بس من غير required. الـ {} valid؟
آه valid. الـ
properties بتقول شكل الحقل لو موجود، لكن مش بتخليه إجباري. لازم required.٢) حطيت default: "user" وAjv مرجّعش role في البيانات. ليه؟
لأن
default annotation بس. Ajv بيحطه فقط لو useDefaults: true.٣) الـ schema { type: "number", minLength: 3 } على 5؟
valid. الـ
minLength بيطبق على strings بس. وAjv بيطبع warning: strict mode: missing type "string" for keyword "minLength".