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:

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-a and not 0/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/natural sub-schema => {"type": "integer", "minimum": 0}
  • validate 58 against 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