Variables
$vars keyword
Variables provide a new way to tackle
different scenarios where JSON schema alone fails.
This means, that you can use a new keyword named $vars
to make your life easier.
$vars can be used as:
- substitution variables for URI templates
- arguments to filters
General structure
In a JSON schema document, $vars must be an object but
the values inside $vars can be anything.
Example of $vars:
{
"$vars": {
"some-number": 123,
"some-data": "data",
"my-array": [1, 2, null, "str"],
"the-object": {
"a": 10,
"b": 5
}
}
}
Data reference values
In the above example, $vars contains only constant values,
making you wonder why it was named like so. The reason is simple,
because you can access the current data being validated using
JSON pointers.
To do this, you will reference a value in the same way you reference
other schemas: by using $ref keyword.
If the $ref cannot be resolved, an error is thrown.
You can prevent this by adding a fallback value
using the default keyword next to the $ref keyword.
Take a look at the next example:
{
"$vars": {
"value-of-prop-a": {"$ref": "/a"},
"secret-value": {
"$ref": "/deep/secret",
"default": "if /deep/secret points to nothing this is the fallback value"
},
"some-constant": 5
}
}
considering that we validate the following data
{
"a": "A",
"deep": {
"secret": "S"
},
"constant": 10
}
the $vars will become
{
"value-of-prop-a": "A",
"secret-value": "S",
"some-constant": 5
}
As you can see, the $ref contains an absolute JSON pointer, but
can also use relative JSON pointers.
Here is an example using a schema:
{
"$id": "http://example.com/vars-example-1#",
"type": "object",
"properties": {
"prop-a": {
"type": "string"
},
"prop-b": {
"$vars": {
"value-of-prop-a": {"$ref": "1/prop-a"},
"some-constant": 5
}
}
}
}
considering that data we validate is
{
"prop-a": "A",
"prop-b": "B"
}
then the $vars will become
{
"value-of-prop-a": "A",
"some-constant": 5
}
You might have some questions right now, but here is a list of Q&A to get a better understanding of what’s happening.
- Why
1/prop-aand not0/prop-a?
Because 0 points to value "A",
which is a string and doesn’t have a prop-a key to descend into, but 1 points to the object
that holds the property prop-a, so it can descend.
- What about
2/prop-a?
Well, in our example it is impossible to ascend 2 levels because at level 1 we are already at the root. So an error will be thrown.
- Where can I find more info about these pointers?
Go to the JSON Pointers page.
- Where is this useful?
See below.
Variables and URI templates
$vars can act as substitutions/variables in URI templates. In other words,
you can dynamically reference schemas based on the current data being validated.
Please note that data can come from an untrusted source,
so it is better to filter it before by using enum or other JSON Schema
keywords including $filters if needed. Also, use URI template
escaping mechanisms.
You can disable uri-templates by setting the allowTemplates option to false.
Here is an example that validates a number by using number’s type.
{
"$id": "http://example.com/number#",
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": ["natural", "integer", "real", "complex"]
},
"value": {
"$ref": "#/definitions/{+number-type}",
"$vars": {
"number-type": {"$ref": "1/type"}
}
}
},
"required": ["type", "value"],
"definitions": {
"natural": {
"type": "integer",
"minimum": 0
},
"integer": {
"type": "integer"
},
"real": {
"type": "number"
},
"complex": {
"type": "object",
"properties": {
"a": {
"type": "number"
},
"b": {
"type": "number"
}
},
"required": ["a", "b"],
"additionalProperties": false
}
}
}
First, we see that $ref of value property is an uri template, having the number-type placeholder.
That placeholder will be replaced with the value of the variable having the same name from $vars.
So, if $vars is {"number-type": "test"} the $ref will become #/definitions/test.
Second, the $ref from number-type property of $vars denotes a relative
JSON pointer. Instead of using the object as placeholder, the value will be
resolved, in our case it will use the value of type property from our data (not schema).
Consider the following data to be validated against our schema:
{"type": "natural", "value": 58}
the steps for validating the value property are:
- get
$ref=>#/definitions/{number-type} - resolve
$vars=>{"number-type": "natural"} - replace placeholders in
$ref=>#/definitions/natural - resolve
#/definitions/naturalsub-schema =>{"type": "integer", "minimum": 0} - validate
58against resolved sub-schema => valid
Global variables
Depending on your project, sometimes you’ll want certain variables
to always be available when validating. Usually, global variables are set from PHP,
but you can override global variables before referencing another schema by
using the $globals keyword.
You can disable $globals keyword by setting the allowGlobals option to false.
Global variables example
{
"type": "object",
"properties": {
"prop-a": {
"$ref": "http://example.com/vendor/{VENDOR_VERSION}/a.json"
},
"prop-b": {
"$ref": "http://example.com/vendor/{VENDOR_VERSION}/b.json",
"$vars": {
"VENDOR_VERSION": "2.0"
}
},
"prop-c": {
"$ref": "http://example.com/vendor/{VENDOR_VERSION}/c.json",
"$vars": {
"version": 5
}
},
"prop-d": {
"$ref": "http://example.com/vendor/{VENDOR_VERSION}/d.json",
"$globals": {
"VENDOR_VERSION": "3.0"
}
}
}
}
Considering that our global variables are:
{
"VENDOR_VERSION": "1.0"
}
the $refs will be
| Property | Value | Reason |
|---|---|---|
prop-a |
http://example.com/vendor/1.0/a.json |
global variable VENDOR_VERSION is 1.0 |
prop-b |
http://example.com/vendor/2.0/b.json |
local variable VENDOR_VERSION is 2.0 |
prop-c |
http://example.com/vendor/1.0/c.json |
global variable VENDOR_VERSION is 1.0 and there is no local variable VENDOR_VERSION |
prop-d |
http://example.com/vendor/1.0/d.json |
global variable VENDOR_VERSION is 1.0 and there is no local variable VENDOR_VERSION. However, inside the referenced schema the value of VENDOR_VERSION will be set to 3.0 |