In the Previous Post
In the previous article, we covered the basics of JSON Schema for validating JSON data.
In this article, we move to advanced usage: combining schemas and applying conditional logic.
Additional Ways to Validate JSON
Validating boolean values
Only true or false is allowed.
{
"type": "boolean"
}
Validating values with enum
With the enum keyword, you can limit allowed values for a field.
The schema below allows only “male” or “female”.
{
"type": "string",
"enum": ["male", "female"]
}
Validating with a constant value
With the const keyword, you can require a field to have a single fixed value.
{
"properties": {
"title": {
"const": "MadPlay's MadLife."
}
}
}
With this schema, the title property must match the predefined string.
Validating null
If type is null, the data must be null.
{
"type": "null"
}
The value must be exactly null. Even an empty string ("") is not accepted.
Composite Schemas
JSON Schema supports composition with the following keywords.
You can list conditions as arrays and evaluate multiple schema rules together.
allOf: all schema validations must pass.anyOf: one or more schema validations must pass.oneOf: exactly one schema validation must pass.
allOf: all schemas must pass
The following schema accepts values that are at most 5 characters and start with a.
So strings like "abc" and "a12a2" pass.
Because no type is declared, numeric values can also pass.
{
"allOf": [
{
"maxLength": 5
},
{
"pattern": "^a"
}
]
}
anyOf: one or more schemas
The schema below validates id when it is either a string or a number.
If id is null or boolean, validation fails.
{
"properties": {
"id": {
"anyOf": [
{
"type": "string"
},
{
"type": "number"
}
]
}
}
}
oneOf: exactly one schema
Only one condition in the list may pass.
In this schema, the value "abc" for id fails because it matches both conditions
(minimum length and starts with a).
{
"properties": {
"id": {
"oneOf": [
{
"minLength": 2
},
{
"pattern": "^a"
}
]
}
}
}
Conditional Schemas
You can also apply conditions with not and if-then-else.
not
Unlike the previous rules, not allows only data that does not match the given schema.
The schema below fails every string value.
{
"not": {
"type": "string"
}
}
This pattern is uncommon, but use it carefully. For example, the rule below can never pass:
{
"type": "string",
"not": {
"type": "string"
}
}
It requires the field to be a string and also rejects all strings.
if-then-else
You can define branch logic with if, then, and else.
The rule is straightforward:
- If
ifmatches, applythen. - If
ifdoes not match, applyelse.
In the following schema, if the data is a string,
it must have at least 3 characters and start with m.
If it is not a string, the value must be 0.
{
"if": {
"type": "string"
},
"then": {
"minLength": 3,
"pattern": "^m"
},
"else": {
"const": 0
}
}
Examples:
"madplay": pass, string, length >= 3, starts withm.0: pass, not a string but equals 0.0.1.1: fail, invalid number.["mad"]: fail, neither string nor 0.
Improving Schema Reusability
Referencing schemas
$id gives a JSON schema a unique identity.
It serves two common purposes.
The first is to declare a unique identifier, often as a download location at the top level:
{
"$id": "http://foo.bar/custom/email-schema.json",
"type": "string",
"format": "email",
"pattern": "@madplay\\.+"
}
The second is to define a base URI that $ref can target:
{
"type": "object",
"properties": {
"name": {
"type": "string",
},
"email": {
"$ref": "http://foo.bar/custom/email-schema.json"
}
}
}
Here, the email field reuses a referenced schema.
Without $ref, the field would embed the full rule directly:
{
"type": "object",
"properties": {
"name": {
"type": "string",
},
"email": {
"type": "string",
"format": "email",
"pattern": "@madplay\\.+"
}
}
}
Defining shared schemas
The definitions keyword is commonly used with $ref for reusable validation blocks.
The example below defines text content and photo content schemas once and reuses them.
Line breaks were reduced to keep the snippet shorter.
{
"type":"object",
"properties":{
"textContents":{
"type":"array",
"items":[{ "$ref":"#/definitions/textContents" }]
},
"paperContents":{
"type":"array",
"items":[{ "$ref":"#/definitions/textContents" }]
},
"photoContents":{
"type":"array",
"items":[{ "$ref":"#/definitions/photoContent" }]
}
},
"definitions":{
"textContents":{
"type":"object",
"properties":{
"id":{ "type":"string" },
"type":{ "type":"string", "const":"text" }
}
},
"photoContent":{
"type":"object",
"properties":{
"id":{ "type":"string" },
"type":{ "type":"string", "enum":[ "photo1", "photo2" ]},
"imageUrl":{ "type":"string" }
},
"required":[ "imageUrl" ]
}
}
}
Next
In this post, we covered more advanced schema composition and schema reuse patterns. In the next post, we will implement a JSON Schema validator in Java code.