Ratgeber · JSON Schema 2020-12
Konditionelle Validation in JSON Schema: if/then/else verständlich erklärt
if/then/else seit Draft 07. Klassischer Use-Case: tagged unions. dependentSchemas als alternative Schreibweise für einfache Pflichtfeld-Logik.
Von Mateusz Viola
Betreiber & redaktionelle Verantwortung json-validator.de
Veröffentlicht
Aktualisiert:
Konditionelle Validation: Wann es sinnvoll ist
Klassischer Use-Case: Tagged Unions / Discriminated Unions. Ein Objekt mit einem Typ-Feld, wo abhängig vom Typ andere Pflichtfelder gelten.
// Free-Plan braucht nur user_id
{ "plan": "free", "user_id": "usr_abc" }
// Pro-Plan braucht zusätzlich billing_email
{ "plan": "pro", "user_id": "usr_def",
"billing_email": "billing@example.com" }
if/then/else (seit Draft 07)
{
"type": "object",
"required": ["plan", "user_id"],
"properties": {
"plan": { "enum": ["free", "pro"] },
"user_id": { "type": "string" },
"billing_email": { "type": "string", "format": "email" }
},
"if": { "properties": { "plan": { "const": "pro" } } },
"then": { "required": ["billing_email"] }
}
Bedeutung: wenn das if-Schema matcht (plan = pro), dann muss auch das then-Schema matchen (billing_email Pflicht). Sonst (else, falls definiert) muss das else-Schema matchen.
Mehrere Bedingungen kombinieren
Du kannst nicht mehrere if/then/else direkt nebeneinander setzen - nur eines pro Schema-Ebene. Workaround: allOf mit mehreren Sub-Schemas:
{
"type": "object",
"required": ["plan"],
"properties": {
"plan": { "enum": ["free", "pro", "enterprise"] }
},
"allOf": [
{
"if": { "properties": { "plan": { "const": "pro" } } },
"then": { "required": ["billing_email"] }
},
{
"if": { "properties": { "plan": { "const": "enterprise" } } },
"then": { "required": ["billing_email", "contract_id"] }
}
]
}
dependentSchemas - die andere Schreibweise
Für einfache "wenn Feld X gesetzt, dann gelten zusätzliche Constraints"-Patterns gibt es dependentSchemas:
{
"type": "object",
"properties": {
"credit_card": { "type": "string" },
"billing_address": { "type": "string" }
},
"dependentSchemas": {
"credit_card": {
"required": ["billing_address"]
}
}
}
Bedeutung: wenn credit_card im Objekt vorhanden ist, dann muss auch billing_address vorhanden sein.
dependentRequired - noch knapper
Wenn die Bedingung nur Pflichtfelder hinzufügt:
{
"type": "object",
"dependentRequired": {
"credit_card": ["billing_address", "cvv"]
}
}
if-Schema: Was es wirklich tut
WICHTIG: Das if-Schema wird nur als Bedingung evaluiert - Fehler im if-Schema produzieren KEINE Validation-Errors. Sie schalten nur zwischen then und else um.
Häufiger Fehler: man setzt im if-Schema "required": [...] und ist überrascht, dass fehlende Pflichtfelder nicht gemeldet werden. Pflichtfelder gehören ins then-Schema oder ins Top-Level-Schema.
Performance-Aspekt
Mehrere if/then/else-Blöcke per allOf sind etwas langsamer als ein einzelnes oneOf mit Discriminator-Optimierung. In Ajv ist der Unterschied messbar aber selten relevant (Mikrosekunden). Code-Lesbarkeit überwiegt typisch.
Alternative: oneOf mit const-Discriminator
{
"oneOf": [
{
"properties": {
"plan": { "const": "free" },
"user_id": { "type": "string" }
},
"required": ["plan", "user_id"]
},
{
"properties": {
"plan": { "const": "pro" },
"user_id": { "type": "string" },
"billing_email": { "type": "string", "format": "email" }
},
"required": ["plan", "user_id", "billing_email"]
}
]
}
Funktioniert auch - aber Properties stehen pro Variante neu. Bei wachsender Schema-Komplexität wird if/then/else lesbarer.
Im Tool oben testen
Setze ein Test-Schema mit if/then/else ein und variiere das Payload. Du siehst sofort welche Constraints wann aktiv werden.