Generic Keywords
JSON Schemas also support keywords that may apply to any JSON type. In this section we provide the correct use and syntax of these keywords.
Combined Restrictions
Sometimes we want to specify more complex constraints that require verifying if a document validates against multiple schemas. JSON Schema allows us to do so using the following keywords:
anyOf
, which forces the document to validate against at least one of the specified schemasallOf
, which forces the document to validate against all of the specified schemasoneOf
, which forces the document to validate against precisely one of the specified schemas.
Additionally, we can use:
not
, which states that a document does not validate against a specified schema.
Below we describe each of the keywords in more detail.
anyOf
The "anyOf"
keyword is used to make document validate against at least one of the listed schemas. For instance, if we were building an application that can accept either strings or integers we could use the following schema to check if the document is of the correct format.
{
"anyOf": [
{ "type": "string" },
{ "type": "integer" }
]
}
In this case JSON document "This is a string"
satisfies the above schema, as does the document 72
. On the other hand 7.42
does not satisfy the schema, nor does the document { "name": "Gary"}
.
allOf
In order to specify that our document has to conform to several different schemas we use the keyword "allOf"
. For instance if we want to accept strings that are in the intersection of two regular expressions it is often simpler to validate against two different schemas than write a single regular expression denoting the intersection. The schema below validates against any string that is a gmail address and that contains a substring "joe"
.
{
"allOf": [
{ "type": "string", "pattern": "^.+@gmail.com$" },
{ "type": "string", "pattern": "^.*joe.*$" }
]
}
Although the example above looks somewhat superficial (and is indeed expressible without using the "allOf"
keyword, as all basic examples of "allOf"
are), the true usefulness of the "allOf"
keyword becomes evident when combined with the "definition"
and "$ref"
keywords.
Note that using "allOf"
one can easily define schemas that do not validate against any document. For instance the schema below requires the document to be both a string and an integer, which is clearly not possible.
{
"allOf": [
{ "type": "string" },
{ "type": "integer" }
]
}
oneOf
When we want to validate against precisely one schema from a predefined set of schemas we use the "oneOf"
keyword. For instance if want to find numbers that are multiples of 2
or multiples of 5
, but not both at the same time we could use the following schema.
{
"oneOf": [
{ "type": "integer", "multipleOf": 2 },
{ "type": "integer", "multipleOf": 5}
]
}
This schema validates against 4
and 15
, but it does not validate against 10
, as it satisfies both of the schemas. Equally, the schema does not validate against 3
, or any document that is not an integer.
not
To say that a document does not validate against a certain schema we use the "not"
keyword. For instance the schema below validates against any document that is not a string.
{ "not": { "type": "string" } }
It accepts the document 4
or { "name": "Gary" }
, but not "name"
, or any other string.
Formal Specification
Combined restrictions combRes are formally specified using the following grammar.
combRes := allOf | anyOf| oneOf | not anyOf := "anyOf": [ { JSch } (, { JSch })* ] allOf := "allOf": [ { JSch } (, { JSch })* ] oneOf := "oneOf": [ { JSch } (, { JSch })* ] not := "not": { JSch }
where JSch denotes an arbitrary JSON Schema.
Formal Validation
Let C be a Combined Restriction and J a JSON document. We say that J validates against C if:
- C is of the form
"anyOf": [{Sch1}, {Sch2}, ... ,{Schn}]
and J conforms to some Schi, for i=1...n - C is of the form
"allOf": [{Sch1}, {Sch2}, ... ,{Schn}]
and J conforms to all of Schi, for i=1...n - C is of the form
"oneOf": [{Sch1}, {Sch2}, ... ,{Schn}]
and J conforms to exactly one of Schi, for i=1...n - C is of the form
"not": {Sch}
and J does not validate against Sch.
Enumerated Values
The "enum"
keyword restricts JSON instances to have certain values specified in an array following the keyword. Let's take a look the following example:
{
"type": "string",
"enum": ["Inter Milan", "AC Milan", "Juventus", "Roma"]
}
Here we are asking for JSON strings with the values "Inter Milan"
, "AC Milan"
, "Juventus"
or "Roma"
. Apart from these, no other JSON string will be validated against the schema. For example the following JSON instance validates against the schema
"AC Milan"
But this one does not
"Fiorentina"
The enum
keyword also allows us have different types in the list of allowed values. Consider the schema:
{
"enum": ["Two", 2, true, null]
}
Then the following JSON validates against the schema:
null
But this one does not:
16
Formal Specification
The "enum"
restriction is formally specified using the following grammar.
enum := "enum": [Jval (, Jval)*]
Where Jval can be either a number
, a string
, an object
, an array
, a boolean
or a null
value. Note that each Jval must be different from the the other values appearing in the array.
Formal Validation
Let S be Schema and J a JSON document. We say that J validates against S if the following holds:
- S has the restriction
"enum": [val1 , ... , valn]
and J equals vali for some i in [ 1 , ..., n ].
Metadata
The following group of keywords represent metadata of the schema and does not have a very strong semantic. However, it is a good practice both for programmers and users to provide this information. These keywords can be either "title"
, "description"
, "default"
or "$schema"
. Let us take a look to the following example
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "Player schema",
"description": "This schema accepts soccer players",
"default": null,
"type": "object",
"properties": {
"first_name": { "type": "string" },
"last_name": { "type": "string" },
"age": { "type": "integer" },
"club": {
"type": "object",
"properties": {
"name": { "type": "string" },
"founded": { "type": "integer" }
},
"required": ["name"]
}
},
"required": ["first_name", "last_name", "age", "club"]
}
First, the "$schema"
keyword specifies that the current schema follows the specification of the version 3 of JSON Schema. We can provide different versions deppending of our requirements. The "title"
keyword is useful to give a name to the schema. Similarly, the "description"
keyword is useful to give a short description of what kind of documents the schema accepts. Finally the "default"
keyword can be used to specify a default value for the document if a hipothetical validator reads a missing value as input.