JSON Schema: $ref, $defs a skládání

JSON Schema skládá podmínky nad stejnými daty. $defs a $ref umožňují pravidla použít opakovaně; logické kombinace vybírají přípustné struktury.

Autor: Aktualizováno

$defs a $ref

Do $defs ulož pojmenovaná schémata. Odkaz $ref je aplikuje na stejnou hodnotu, kterou právě validuješ. Lokální fragment #/$defs/zakaznik adresuje schéma uvnitř tohoto dokumentu, nikoli vlastnost v datech.

Znovupoužitelný základ a uzavřený výsledek
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$defs": {
    "zakaznik": {
      "type": "object",
      "properties": {
        "jmeno": {
          "type": "string"
        }
      },
      "required": [
        "jmeno"
      ]
    }
  },
  "allOf": [
    {
      "$ref": "#/$defs/zakaznik"
    }
  ],
  "properties": {
    "aktivni": {
      "type": "boolean"
    }
  },
  "required": [
    "aktivni"
  ],
  "unevaluatedProperties": false
}
Platná data
{"jmeno":"Jana","aktivni":true}

V 2020-12 se vyhodnocují i další klíčová slova vedle $ref. V Draft 7 byla vedle odkazu ignorována. Vnořené $id mění základ URI a hranici zdroje schématu; při přemísťování definic proto kontroluj odkazy.

Logické kombinace

SlovoPodmínka
allOfmusí vyhovět každému schématu
anyOfmusí vyhovět alespoň jednomu
oneOfmusí vyhovět právě jednomu
notnesmí vyhovět zadanému schématu

allOf nepřepisuje ani neslučuje definice jako dědičnost v programovacím jazyce. Všechny podmínky musí platit současně. Kombinace type: string a type: number přes allOf nepřijme žádnou hodnotu.

Osoba, nebo firma
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "oneOf": [
    {
      "type": "object",
      "properties": {
        "druh": {
          "const": "osoba"
        },
        "jmeno": {
          "type": "string"
        }
      },
      "required": [
        "druh",
        "jmeno"
      ]
    },
    {
      "type": "object",
      "properties": {
        "druh": {
          "const": "firma"
        },
        "ico": {
          "type": "string",
          "pattern": "^[0-9]{8}$"
        }
      },
      "required": [
        "druh",
        "ico"
      ]
    }
  ],
  "unevaluatedProperties": false
}

Rozlišovací vlastnost druh zajišťuje, že větve nemohou uspět zároveň. Bez ní může například číslo vyhovět větvi number i integer, a oneOf ho odmítne. Pokud chceš překryv dovolit, použij anyOf.

Proč additionalProperties nestačí při rozšiřování

additionalProperties vidí deklarace ve svém dílčím schématu. Pokud uzavřeš základ zákazníka, odmítne aktivni i tehdy, když ho jiná větev allOf povoluje. Uzavři až výsledek pomocí unevaluatedProperties: false, jako v prvním příkladu. To vyžaduje dialekt 2019-09 nebo novější a podporu validátoru.

Úspěšně vyhodnocené vlastnosti se sbírají podle pravidel aplikátorů. Neznamená to, že jakákoli zmínka o názvu v neúspěšné větvi automaticky dovolí vlastnost.

Externí odkazy a doporučené postupy

$ref může odkazovat také na jiný zdroj. Aplikace má schémata registrovat pod očekávanými URI a určit, které zdroje smí načíst. Online nástroj XML.cz externí schémata automaticky nestahuje; pro ukázky používej lokální $defs.

  • Ve znovu použitelných dílčích schématech ponech další pole povolená. Zakaž je až ve výsledném schématu konkrétního vstupu.
  • Každou alternativu otestuj platnými i chybnými daty.
  • Omez hloubku a velikost dat a složitost schémat, zejména u rekurze.
  • Změnu dialektu ber jako migraci a ověř ji podle přehledu verzí.

oneOf a anyOf: proč celé číslo může selhat

Dvě překrývající se větve
{"oneOf":[{"type":"number"},{"type":"integer"}]}

Hodnota 1.5 projde pouze number a je platná. Hodnota 1 projde number i integer a oneOf ji odmítne. Po změně na anyOf projdou obě. oneOf počítá úspěšné větve, nevybírá automaticky „nejlepší typ“. U zpráv používej rozlišovací vlastnost s const a required v každé větvi.

Lokální rekurzivní schéma

Strom pojmenovaných uzlů
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$defs": {
    "uzel": {
      "type": "object",
      "required": [
        "nazev"
      ],
      "properties": {
        "nazev": {
          "type": "string"
        },
        "deti": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/uzel"
          }
        }
      },
      "additionalProperties": false
    }
  },
  "$ref": "#/$defs/uzel"
}
Platná instance stromu
{"nazev":"XML","deti":[{"nazev":"XPath"},{"nazev":"XSD"}]}

Odkaz v items aplikuje stejný typ na každého potomka. Dítě bez nazev neprojde required. Prázdné deti je platné a vynechané deti také; přítomnost seznamu není povinná. Rekurze schématu nevytváří sama cyklická JSON data, protože JSON text cyklické odkazy neobsahuje.

Omez hloubku a počet uzlů v aplikaci. Pro rozšiřitelnou rekurzi existují dynamické odkazy, jejichž chování závisí na dialektu; lokální $ref v této ukázce má jednoduchý pevný cíl. $id je identifikátor a základ URI schématu, nikoli příkaz automaticky stahovat soubor.