Quick start
Some quick examples about how to use Opis JSON Schema
To better exemplify the benefits of using JSON Schema for validating JSON documents, were gonna build a validation
schema for a set of data representing the profile of some web application’s user.
For the sake of simplicity we don’t care how we obtained these data, and we will simply assume that they are stored
in a variable called $data
.
Here is a possible content for this variable:
{
"name": "John Doe",
"age": 31,
"email": "john@example.com",
"website": null,
"location": {
"country": "US",
"address": "Sesame Street, no. 5"
},
"available_for_hire": true,
"interests": ["php", "html", "css", "javascript", "programming", "web design"],
"skills": [
{
"name": "HTML",
"value": 100
},
{
"name": "PHP",
"value": 55
},
{
"name": "CSS",
"value": 99.5
},
{
"name": "JavaScript",
"value": 75
}
]
}
The validation schema
Now that we have an idea over how our data are structured, its time to start creating a validation schema.
The JSON contained in $data
is structured as an object with multiple
properties. All the properties are required to be present and adding additional properties is forbidden.
The validation schema that follows these rules, looks something like bellow.
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "http://api.example.com/profile.json",
"type": "object",
"properties": {
},
"required": ["name", "age", "email", "location",
"available_for_hire", "interests", "skills"],
"additionalProperties": false
}
Neither the $schema, nor the $id keywords are required in order for our validation schema to work. We only added them as an example of good practice.
Object’s properties
The only thing that is missing from the above validation schema are the validation rules for its properties. Let’s take each of the object’s property in turn, explain it, and write some validation rules for them.
name
The name
property must be a non-empty string that contains at most 64 characters.
We also added a pattern constraint, to make sure that the value of the name
property
follows a specific format.
{
"name": {
"type": "string",
"minLength": 1,
"maxLength": 64,
"pattern": "^[a-zA-Z0-9\\-]+(\\s[a-zA-Z0-9\\-]+)*$"
}
}
age
The age
property is an integer between 18 and 100.
{
"age": {
"type": "integer",
"minimum": 18,
"maximum": 100
}
}
This is a string that must be formatted as an email and its maximum length is of 128 characters.
{
"email": {
"type": "string",
"maxLength": 128,
"format": "email"
}
}
website
If the user doesn’t have a website, then the value of this property must be null
. Otherwise, this is
a string of a maximum length of 128 characters, formatted as a hostname.
{
"website": {
"type": ["string", "null"],
"maxLength": 128,
"format": "hostname"
}
}
location
This property is an object that contains two other properties: country
and address
.
The country
property is a two-letter string representing the country’s code.
To keep things simple we only support United State, Canada and United Kingdom.
Therefore we could just use an enum. The address
property
is just a string that can contains at most 128 characters.
Both these properties are required, and the object doesn’t support additional properties.
{
"location": {
"type": "object",
"properties": {
"country": {
"enum": ["US", "CA", "GB"]
},
"address": {
"type": "string",
"maxLength": 128
}
},
"required": ["country", "address"],
"additionalProperties": false
}
}
available_for_hire
The property’s value must be a boolean
{
"available_for_hire": {
"type": "boolean"
}
}
interests
This must be an array of strings. It must contain at least 3 items and a maximum of 100. Each string within the array must have a maximum length of 64 characters and must be unique to the array.
{
"interests": {
"type": "array",
"minItems": 3,
"maxItems": 100,
"uniqueItems": true,
"items": {
"type": "string",
"maxLength": 64
}
}
}
skills
This is also an array, but this one contains objects. Each object has two required properties: name
and value
.
The name
properties represents the name of the skill that user have, and it’s a non-empty string of a maximum
length of 64 characters. The value
property tells how skilled is the user using a float number between 0 and 100.
That number can be expressed as multiples of 0.25. Each object within the array must be unique and it doesn’t
accept new propeties.
There isn’t a minimum number of skills that a user can have, so the array can be empty,
but there is a maximum of 100 skills that can be declared.
{
"skills": {
"type": "array",
"maxItems": 100,
"uniqueItems": true,
"items": {
"type": "object",
"properties": {
"name": {
"type": "string",
"minLenght": 1,
"maxLength": 64
},
"value": {
"type": "number",
"minimum": 0,
"maximum": 100,
"multipleOf": 0.25
}
},
"required": ["name", "value"],
"additionalProperties": false
}
}
}
Putting all together
Now that we have finished defining validation rules, let’s put all together and see the resulting validation schema.
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "http://api.example.com/profile.json",
"type": "object",
"properties": {
"name": {
"type": "string",
"minLength": 1,
"maxLength": 64,
"pattern": "^[a-zA-Z0-9\\-]+(\\s[a-zA-Z0-9\\-]+)*$"
},
"age": {
"type": "integer",
"minimum": 18,
"maximum": 100
},
"email": {
"type": "string",
"maxLength": 128,
"format": "email"
},
"website": {
"type": ["string", "null"],
"maxLength": 128,
"format": "hostname"
},
"location": {
"type": "object",
"properties": {
"country": {
"enum": ["US", "CA", "GB"]
},
"address": {
"type": "string",
"maxLength": 128
}
},
"required": ["country", "address"],
"additionalProperties": false
},
"available_for_hire": {
"type": "boolean"
},
"interests": {
"type": "array",
"minItems": 3,
"maxItems": 100,
"uniqueItems": true,
"items": {
"type": "string",
"maxLength": 64
}
},
"skills": {
"type": "array",
"maxItems": 100,
"uniqueItems": true,
"items": {
"type": "object",
"properties": {
"name": {
"type": "string",
"minLenght": 1,
"maxLength": 64
},
"value": {
"type": "number",
"minimum": 0,
"maximum": 100,
"multipleOf": 0.25
}
},
"required": ["name", "value"],
"additionalProperties": false
}
}
},
"required": ["name", "age", "email", "location",
"available_for_hire", "interests", "skills"],
"additionalProperties": false
}
The PHP part
Believe it or not, the PHP part is the most trivial part of the validation process.
Once we have the validation schema, we can save it into a file like schema.json
and
start validating the content of $data
variable.
use Opis\JsonSchema\{
Validator,
ValidationResult,
Errors\ErrorFormatter,
};
// Create a new validator
$validator = new Validator();
// Register our schema
$validator->resolver()->registerFile(
'http://api.example.com/profile.json',
'/path/to/schema.json'
);
$data = <<<'JSON'
{
"name": "John Doe",
"age": 15,
... rest of the properties
}
JSON;
// Decode $data
$data = json_decode($data);
/** @var ValidationResult $result */
$result = $validator->validate($data, 'http://api.example.com/profile.json');
if ($result->isValid()) {
echo "Valid", PHP_EOL;
} else {
// Print errors
print_r((new ErrorFormatter())->format($result->error()));
}