FAQ k dotazování a validaci: JSONPath a Schema
Shrnutí: Zda má dotazování cest používat JSONPath, nebo Pointer, jak volit mezi Patch a Merge Patch, co Schema umí zablokovat a jak vybrat verzi návrhu – vše v jednom průchodu.

Stručně: JSONPath použijte k nalezení uzlů podle podmínky, JSON Pointer nebo JSON Patch k adresování a úpravě přesných umístění a Merge Patch ke sloučení konfigurace; nová Schema zafixujte na 2020-12 a zapněte allErrors, aby se všechny poruchy vrátily v jednom průchodu.
Dotazování je „najdi to“; validace je „potvrď, že to je správně“. Každé má svůj standard, takže je nemíchejte.
1. Dotazování cest
Jak volit mezi JSONPath a JSON Pointer?
Záleží na tom, zda „hledáte podle podmínky“, nebo „načítáte podle pozice“.
| Dimenze | JSONPath | JSON Pointer |
|---|---|---|
| Standard | RFC 9535 (2024) | RFC 6901 (2013) |
| Výraz | $.users[?(@.age > 18)].id |
/users/0/id |
| Zástupné znaky a rekurze | Podporuje *, .. |
Nepodporováno |
| Podmíněné filtrování | Podporováno | Nepodporováno |
| Výsledek | Množina uzlů (možná několik) | Jedno umístění |
| Vyjádřivost | Silná, ale s těžkým dědictvím rozdílů mezi implementacemi | Slabá, ale plně deterministická |
| Typické použití | Extrakce z logů, ladění, průzkum dat | JSON Patch, adresování konfigurace, cesty chyb |
Pravidlo volby je přímočaré:
- Potřebujete „získat všechny uzly odpovídající podmíně“ → JSONPath;
- Potřebujete „určit jedno přesné umístění a přidat, odebrat nebo upravit ho“ → JSON Pointer;
- Použijte je spolu: najděte uzel pomocí JSONPath a popište jeho umístění pomocí Pointer.
Nepoužívejte JSONPath pro adresování ve smlouvě API. JSONPath vrací množinu a tři větve pro prázdný, jednočlenný a vícečlenný výsledek protokol komplikují; Pointer znamená „buď ukazuje na existující umístění, nebo neexistuje“, což se ke smlouvě hodí lépe.
Jaký je rozdíl mezi $.. a $.*?
$.. je rekurzivní sestup a prochází každou úroveň; $.* vrací jen přímé potomky aktuální úrovně.
{
"a": { "id": 1, "child": { "id": 2 } },
"b": { "id": 3 }
}
$.*→ dva objekty{id:1, child:{...}}a{id:3};$..id→ tři hodnoty:1,2,3;$.*.id→1a3, bez vnořeného2.
Rekurzivní sestup je šikovný, ale drahý: $.. prochází celý strom, což je u velkého dokumentu O(n), a snadno sebere hluboká pole, která jste nezamýšleli.
Pokud funguje konkrétní úroveň, vyhněte se ...
Umějí filtry porovnávat řetězce? Umějí párovat regulárním výrazem?
Porovnání řetězců funguje; regulární výraz závisí na implementaci.
Standardní syntaxe (RFC 9535):
$.items[?(@.status == "active")]
$.items[?(@.price > 10 && @.stock > 0)]
$.items[?(@.name == $.target)] // porovnání s jiným uzelm
Porovnání řetězců používá dvojité uvozovky (RFC 9535 povoluje i jednoduché, ale některé starší implementace přijímají jen dvojité).
Číselné porovnání se řídí číselnou sémantikou; míchání řetězce s číslem, například "10" > 9, je ve standardu nedefinované chování, takže se na něj nespoléhejte.
Pro regulární výrazy RFC 9535 definuje funkce match() a search(), ale jejich podpora se mezi implementacemi značně liší.
Bezpečný přístup: nechte JSONPath na hrubé filtrování a párování regulárním výrazem udělejte v kódu po získání výsledků.
Mám použít JSON Patch, nebo Merge Patch?
Oba popisují „jak něco změnit“, ale jejich vyjádřivost a sémantika jsou zcela odlišné.
| Dimenze | JSON Patch (RFC 6902) | Merge Patch (RFC 7386) |
|---|---|---|
| Forma | Pole operací [{"op":"add",...}] |
Částečný objekt |
| Podporované operace | add / remove / replace / move / copy / test | Jen sloučení (smazání pomocí null) |
| Adresování prvků pole | Ano, podle indexu | Ne, celé pole se nahradí |
| Atomicita | test umožňuje optimistické zamykání |
Žádná |
| Velikost dokumentu | Velká, ale přesná | Malá, ale omezená vyjádřivost |
| Hodí se pro | Jemnozrnné úpravy, částečné aktualizace API | Slučování konfigurace, částečné odeslání formuláře |
Kritéria rozhodování:
- Změnit pole třetího prvku v poli → JSON Patch;
- Sloučit několik konfiguračních položek do existující konfigurace → Merge Patch;
- Získat řízení souběžnosti „selhat, když původní hodnota není X“ →
testv JSON Patch.
Pozor na past sémantiky null v Merge Patch: null znamená „smaž tento klíč“, nikoli „nastav hodnotu na null“.
Nastavit hodnotu na null je v Merge Patch nemožné.
2. Validace schématem
Jaké problémy JSON Schema řeší?
Mění „jak mají data vypadat“ z verbální dohody na strojově kontrolovatelnou smlouvu. Chyby, které skutečně umí zablokovat:
- Chybějící povinná pole (
required); - Špatný typ, například řetězec tam, kde se čeká číslo (
type); - Hodnoty mimo rozsah (
minimum/maximum/minLength); - Hodnoty mimo povolenou množinu (
enum); - Nekonzistentní typy prvků pole nebo špatná délka pole (
items/minItems); - Chybějící pole ve vnořených strukturách (rekurzivní validace přes
properties); - Nadbytečná pole (
additionalProperties: false).
Co neumí vyřešit: obchodní pravidla („čas konce musí být pozdější než čas začátku“), podmínky napříč poli (některé lze vyjádřit pomocí if/then,
ale čitelnost trpí), jedinečnost v databázi a kontroly oprávnění.
Schema je tedy vrátný pro „tvar dat“, nikoli náhrada obchodní logiky.
Mám použít draft-07, nebo 2020-12?
Pro nové projekty použijte 2020-12; u existujících systémů zkontrolujte, co validátor podporuje.
| Verze | Stav | Klíčové změny |
|---|---|---|
| draft-04 | Zastaralý | Mnoho starších knihoven ho stále používá jako výchozí |
| draft-07 | De facto standard | Nejširší podpora, nejbohatší ekosystém |
| 2019-09 | Přechodný | Zavedl $defs, unevaluatedProperties |
| 2020-12 | Aktuálně doporučený | items rozděleno na prefixItems + items, podporuje $dynamicRef |
Dvě migrační pasti:
- Syntaxe n-tic se změnila: v draft-07 pole-hodnota
itemsvyjadřovalo n-tici; 2020-12 to přesunulo doprefixItems; $refa související klíčová slova: v draft-07 se související klíčová slova ignorují, když je přítomno$ref; 2020-12 umožňuje jejich koexistenci.
Pokud validátor podporuje jen draft-07, psaní syntaxe 2020-12 i přesto je tiše ignorováno – a nejnebezpečnější případ je „validace prošla“, když se nevalidovalo vůbec nic.
Jak generovat typy TypeScript ze Schema?
Dvě cesty: odvodit typy ze Schema, nebo generovat Schema z typů.
Ze Schema do typů (backend definuje kontrakt, frontend ho konzumuje):
import type { FromSchema } from 'json-schema-to-ts';
const userSchema = {
type: 'object',
properties: {
id: { type: 'string' },
age: { type: 'integer' },
},
required: ['id'],
additionalProperties: false,
} as const;
type User = FromSchema<typeof userSchema>;
// { id: string; age?: number }
Klíčové je as const: bez něj se type: 'string' odvodí jako string, nikoli jako literál 'string', a odvozování se rozpadne.
Z typů do Schema (frontend už typy má a potřebuje validaci za běhu):
import { z } from 'zod';
import { zodToJsonSchema } from 'zod-to-json-schema';
const userSchema = z.object({ id: z.string(), age: z.number().int().optional() });
const jsonSchema = zodToJsonSchema(userSchema);
Výhodou této cesty je, že „jedna definice vydá jak typy, tak validaci za běhu“; cenou je zavádění DSL
a vygenerované Schema může nést detaily implementace (například neatraktivní strukturu $ref).
Jak při selhání validace lokalizovat konkrétní pole?
Podívejte se na instancePath (2020-12) nebo dataPath (draft-07), které validátor vrátil; je to JSON Pointer na místo chyby.
[
{
"instancePath": "/items/2/price",
"schemaPath": "#/properties/items/items/properties/price/type",
"keyword": "type",
"message": "must be number"
}
]
instancePath: "/items/2/price" vám přímo řekne: pole price třetího prvku v poli items má špatný typ.
Dvě praktické rady:
- Vracejte všechny chyby najednou, místo abyste vyhazovali u první – oprava všeho frontendem v jednom průchodu je rychlejší než zkoušení tam a zpět;
- Přeložte Pointer do běžného jazyka, než ho ukážete koncovým uživatelům;
/items/2/priceje užitečné pro vývojáře a pro uživatele zbytečné.
allErrors: true je přepínač ve většině validátorů (Ajv ho vyžaduje explicitně zapnout); výchozí nastavení je často režim fail-fast.
Má se zapnout additionalProperties: false?
Zapněte ho pro interní kontrakty; u veřejných API buďte opatrní.
Po zapnutí selže validace každého pole, které není deklarováno pod properties. Přínosem je zachycení překlepů
(userNmae) a nečekaného procházení; nevýhodou je, že přidání pole upstreamem rozbije downstream konzumenty a zničí zpětnou kompatibilitu.
Kompromis:
| Scénář | Doporučení |
|---|---|
| Mezi interními službami | Zapnout. Změny polí procházejí verzováním |
| Veřejná externí API | Vypnout. Nechte upstream přidávat pole |
| Konzumace dat třetích stran | Vypnout. Protější stranu nekontrolujete |
| Validace konfigurace | Zapnout. Překlep v konfiguraci má selhat okamžitě |
Jak validovat, že prvky pole jsou jedinečné?
Dvě různé sémantiky – nezaměňujte je:
{ "uniqueItems": true }
Toto deduplikuje celý prvek, tedy vyžaduje, aby pole neobsahovalo dva hluboce shodné prvky.
{
"type": "array",
"items": { "type": "object", "properties": { "id": { "type": "string" } } },
"uniqueItems": true
}
Úryvek výše zaručuje jen, že objekty jako celek nejsou duplikované; {"id":"a","name":"x"} a {"id":"a","name":"y"} se za duplikáty nepovažují.
Deduplikaci podle konkrétního pole Schema vyjádřit neumí; musíte validovat v obchodní vrstvě:
const ids = items.map((item) => item.id);
if (new Set(ids).size !== ids.length) {
throw new Error('id 重复');
}
Validuje format skutečně e-maily a data?
Ne nutně. Ve specifikaci je format ve výchozím nastavení „anotace“, nikoli „assertion“, takže validátor ho může sbírat, aniž by ho validoval.
- Ajv vyžaduje instalaci a konfiguraci samostatného balíčku
ajv-formats, jinak jeformat: "email"fakticky neúčinný; - Některé validátory považují
formatza assertion zapnutý ve výchozím nastavení, takže chování je nekonzistentní; - I když je zapnutý, regulární výraz pro
emailje velmi volný –a@bprojde.
Závěr: nepovažujte format za silnou validaci. Přísnou validaci e-mailů, URL a dat dělejte v obchodním kódu;
držet format ve Schema jako dokumentaci má smysl, ale nečekejte, že zastaví špinavá data.
3. Rychlý přehled
| Otázka | Závěr |
|---|---|
| Najít uzly podle podmínky | JSONPath |
| Přesné adresování, aplikace patchů | JSON Pointer |
| Změnit prvky pole | JSON Patch, Merge Patch to neumí |
| Sloučení konfigurace | Merge Patch |
| Verze Schema pro nové projekty | 2020-12, pozor na prefixItems |
| Generovat typy TS ze Schema | json-schema-to-ts + as const |
| Lokalizovat validační chyby | Přečtěte instancePath, zapněte allErrors |
| Deduplikace podle pole | Schema neumí; použijte Set v obchodní vrstvě |
format: email |
Většina validátorů ho ve výchozím nastavení nekontroluje, nespoléhejte se na něj |