JSON Schema Notes

Example

{
  "first_name": "George",
  "last_name": "Washington",
  "birthday": "1732-02-22",
  "address": {
    "street_address": "3200 Mount Vernon Memorial Highway",
    "city": "Mount Vernon",
    "state": "Virginia",
    "country": "United States"
  }
}

schema:

{
  "$schema": "http://json-schema.org/schema#",
  "$id": "http://yourdomain.com/schemas/myschema.json",
  "type": "object",
  "properties": {
  "first_name": { "type": "string" },
  "last_name": { "type": "string" },
  "birthday": { "type": "string", "format": "date-time" },
  "address": {
    "type": "object",
    "properties": {
        "street_address": { "type": "string" },
        "city": { "type": "string" },
        "state": { "type": "string" },
        "country": { "type" : "string" }
      }
    }
  }
}

Basics

Schema:

{ }
or
true

matches any well-formed JSON document!

false

matches no JSON document!

{ "type": "string" }

matches any string

{ "type": "number" }

matches any number

{ "type": ["number", "string"] }

matches any number OR string

{
  "type": "string",
  "minLength": 2,
  "maxLength": 3
}

matches "ABC" but not "ABCD"

{
  "type": "string",
  "pattern": "^(\\([0-9]{3}\\))?[0-9]{3}-[0-9]{4}$"
}

matches "555-1212" or "(888)555-1212" but not "(888)555-1212 ext. 532"

{ "type": "integer" }

{
  "type" : "number",
  "multipleOf" : 10
}

{
  "type": "number",
  "minimum": 0,
  "exclusiveMaximum": 100
}


{ "type": "object" }

{
  "type": "object",
  "properties": {
    "number": { "type": "number" },
    "street_name": { "type": "string" },
    "street_type": { 
      "type": "string",
      "enum": ["Street", "Avenue", "Boulevard"]
    }
  }
}

matches

{ "number": 1600, "street_name": "Pennsylvania", "street_type": "Avenue" }
or
{ "number": 1600, "street_name": "Pennsylvania" }
or
{ "number": 1600, "street_name": "Pennsylvania", "street_type": "Avenue", "direction": "NW" }

leaving out properties is OK 
additional properties are OK

{
  "type": "object",
  "properties": {
    "number": { "type": "number" },
    "street_name": { "type": "string" },
    "street_type": { 
      "type": "string",
      "enum": ["Street", "Avenue", "Boulevard"]
    }
  },
  "additionalProperties": false
}

will not match JSON with additional properties.

{
  "type": "object",
  "properties": {
    "number": { "type": "number" },
    "street_name": { "type": "string" },
    "street_type": {
      "type": "string",
      "enum": ["Street", "Avenue", "Boulevard"]
    }
  },
  "additionalProperties": {"type": "string"}
}

matches with additional properties only if they are strings.

{
  "type": "object",
  "properties": {
    "name": { "type": "string" },
    "email": { "type": "string" },
    "address": { "type": "string" },
    "telephone": { "type": "string" }
  },
  "required": ["name", "email"]
}

Built-in formats:

"date-time": Date representation, as defined by RFC 3339, section 5.6.

"email": Internet email address, see RFC 5322, section 3.4.1.

"hostname": Internet host name, see RFC 1034, section 3.1.

"ipv4": IPv4 address, according to dotted-quad ABNF syntax as defined in RFC 2673, section 3.2.

"ipv6": IPv6 address, as defined in RFC 2373, section 2.2.

"uri": A universal resource identifier (URI), according to RFC3986.

"uri-reference": New in draft 6 A URI Reference (either a URI or a relative-reference), according to RFC3986, section 4.1.

Numeric Types

{ "type": "integer" }
{ "type": "number", "multipleOf": 1.0 }
{ "type": "number" }
{
  "type" : "number",
  "multipleOf" : 10
}

{
  "type": "number",
  "minimum": 0,
  "exclusiveMaximum": 100
}

Object Type

{ "type": "object" }

{
  "type": "object",
  "properties": {
    "number": { "type": "number" },
    "street_name": { "type": "string" },
    "street_type": { 
      "type": "string",
      "enum": ["Street", "Avenue", "Boulevard"]
    }
  }
}

Restrictions on property names and number of properties:

{
  "type": "object",
  "propertyNames": {
    "pattern": "^[A-Za-z_][A-Za-z0-9_]*$"
  }
}

{
  "type": "object",
  "minProperties": 2,
  "maxProperties": 3
}

Property Dependencies:

{
  "type": "object",
  "properties": {
    "name": { "type": "string" },
    "credit_card": { "type": "number" },
    "billing_address": { "type": "string" }
  },
  "required": ["name"],
  "dependencies": {
    "credit_card": ["billing_address"]
  }
}

{
  "type": "object",
  "properties": {
  "name": { "type": "string" },
  "credit_card": { "type": "number" },
  "billing_address": { "type": "string" }
  },
  "required": ["name"],
  "dependencies": {
    "credit_card": ["billing_address"],
    "billing_address": ["credit_card"]
  }
}

Schema dependencies:

{
  "type": "object",
  "properties": {
    "name": { "type": "string" },
    "credit_card": { "type": "number" }
  },
  "required": ["name"],
  "dependencies": {
    "credit_card": {
      "properties": {
        "billing_address": { "type": "string" }
      },
      "required": ["billing_address"]
    }
  }
}

matches

{
  "name": "John Doe",
  "credit_card": 5555555555555555,
  "billing_address": "555 Debtor's Lane"
}

and

{
  "name": "John Doe",
  "billing_address": "555 Debtor's Lane"
}

but not

{
  "name": "John Doe",
  "credit_card": 5555555555555555
}

Pattern Properties:

{
  "type": "object",
  "patternProperties": {
    "^S_": { "type": "string" },
    "^I_": { "type": "integer" }
  },
  "additionalProperties": false
}

matches

{ "S_25": "This is a string" }
{ "I_0": 42 }
but not
{ "S_0": 42 }

{
  "type": "object",
  "properties": {
    "builtin": { "type": "number" }
  },
  "patternProperties": {
    "^S_": { "type": "string" },
    "^I_": { "type": "integer" }
  },
  "additionalProperties": { "type": "string" }
}

matches

{ "builtin": 42 }
{ "keyword": "value" }
but not
{ "keyword": 42 }

Arrays:

{ "type": "array" }

matches

[1, 2, 3, 4, 5]
[3, "different", { "types" : "of values" }]
but not
{"Not": "an array"}

Array Items:

{
  "type": "array",
  "items": {
    "type": "number"
  }
}

[1, 2, 3, 4, 5] ok
[1, 2, "3", 4, 5] not ok

{
  "type": "array",
  "contains": {
    "type": "number"
  }
}

["life", "universe", "everything", 42] ok
["life", "universe", "everything", "forty-two"] not ok

Tuple validation:

{
  "type": "array",
  "items": [
    {
      "type": "number"
    },
    {
      "type": "string"
    },
    {
      "type": "string",
      "enum": ["Street", "Avenue", "Boulevard"]
    },
    {
      "type": "string",
      "enum": ["NW", "NE", "SW", "SE"]
    }
  ]
}

matches

[1600, "Pennsylvania", "Avenue", "NW"]
[10, "Downing", "Street"]
[1600, "Pennsylvania", "Avenue", "NW", "Washington"]

can add 
"additionalItems": false
at the end to disallow "Washington"
or
"additionalItems": { "type": "string" }
to allow additional string items in the array

array lengths:

{
  "type": "array",
  "minItems": 2,
  "maxItems": 3
}

{
  "type": "array",
  "uniqueItems": true
}

no duplicates allowed


Boolean type:

{ "type": "boolean" }

true
false

Metadata:

title: title of schema
description: description of schema
default: default value for missing required items
examples: examples of json that should validate 


{
  "title" : "Match anything",
  "description" : "This is a schema that matches anything.",
  "default" : "Default value",
  "examples" : [ "Anything", 4035 ]
}

enumerated type:

{
  "type": "string",
  "enum": ["red", "amber", "green"]
}

{
  "enum": ["red", "amber", "green", null, 42]
}

Combining Schemas:
anyOf, allOf, oneOf, not

{
  "anyOf": [
    { "type": "string", "maxLength": 5 },
    { "type": "number", "minimum": 0 }
  ]
}

"short" ok
12 ok
"too long" not ok

{
  "allOf": [
    { "type": "string" },
    { "maxLength": 5 }
  ]
}

{
  "allOf": [
    { "type": "string" },
    { "type": "number" }
  ]
}

same as

false

{
  "oneOf": [
    { "type": "number", "multipleOf": 5 },
    { "type": "number", "multipleOf": 3 }
  ]
}

exactly one
10 ok
9 ok
2 not ok
15 not ok

{ "not": { "type": "string" } }

Pattern:

• A single unicode character (other than the special characters below) matches itself.
• ^: Matches only at the beginning of the string.
• $: Matches only at the end of the string.
• (...): Group a series of regular expressions into a single regular expression.
• |: Matches either the regular expression preceding or following the | symbol.
• [abc]: Matches any of the characters inside the square brackets.
• [a-z]: Matches the range of characters.
• [^abc]: Matches any character not listed.
• [^a-z]: Matches any character outside of the range.
• +: Matches one or more repetitions of the preceding regular expression.
• *: Matches zero or more repetitions of the preceding regular expression.
• ?: Matches zero or one repetitions of the preceding regular expression.
• +?, *?, ??: The *, +, and ? qualifiers are all greedy; they match as much text as possible. Sometimes this behavior
isn’t desired and you want to match as few characters as possible.
• (?!x}, (?=x}: Negative and positive lookahead.
• {x}: Match exactly x occurrences of the preceding regular expression.
• {x,y}: Match at least x and at most y occurrences of the preceding regular expression.
• {x,}: Match x occurrences or more of the preceding regular expression.
• {x}?, {x,y}?, {x,}?: Lazy versions of the above expressions.


{
  "type": "string",
  "pattern": "^(\\([0-9]{3}\\))?[0-9]{3}-[0-9]{4}$"
}

Reuse (definitions)

{
  "type": "object",
  "properties": {
    "street_address": { "type": "string" },
    "city": { "type": "string" },
    "state": { "type": "string" }
  },
  "required": ["street_address", "city", "state"]
}

You can place the above in the "definitions" section:

{
  "definitions": {
    "address": {
      "type": "object",
      "properties": {
        "street_address": { "type": "string" },
        "city": { "type": "string" },
        "state": { "type": "string" }
      },
    "required": ["street_address", "city", "state"]
    }
  }
}

and refer to it as:

{ "$ref": "#/definitions/address" }

or if definitions is in a separate file:

{ "$ref": "definitions.json#/address" }

More interesting example:

{
  "$schema": "http://json-schema.org/draft-06/schema#",
  "definitions": {
    "address": {
      "type": "object",
      "properties": {
        "street_address": { "type": "string" },
        "city": { "type": "string" },
        "state": { "type": "string" }
      },
      "required": ["street_address", "city", "state"]
    }
  },
  "type": "object",
  "properties": {
    "billing_address": { "$ref": "#/definitions/address" },
    "shipping_address": { "$ref": "#/definitions/address" }
  }
}

matches

{
  "shipping_address": {
    "street_address": "1600 Pennsylvania Avenue NW",
    "city": "Washington",
    "state": "DC"
  },
  "billing_address": {
    "street_address": "1st Street SE",
    "city": "Washington",
    "state": "DC"
  }
}

Recursion:

{
  "$schema": "http://json-schema.org/draft-06/schema#",
  "definitions": {
    "person": {
      "type": "object",
      "properties": {
        "name": { "type": "string" },
        "children": {
          "type": "array",
          "items": { "$ref": "#/definitions/person" },
          "default": []
        }
      }
    }
  },
  "type": "object",
   properties": {
    "person": { "$ref": "#/definitions/person" }
  }
}

matches:

{
  "person": {
    "name": "Elizabeth",
    "children": [
      {
        "name": "Charles",
        "children": [
          {
            "name": "William",
            "children": [
              { "name": "George" },
              { "name": "Charlotte" }
            ]
          },
          {
            "name": "Harry"
          }
        ]
      }
    ]
  }
}

Following would cause a problem (loop??)

{
  "definitions": {
    "alice": {
      "anyOf": [
        { "$ref": "#/definitions/bob" }
      ]
    },
    "bob": {
      "anyOf": [
        { "$ref": "#/definitions/alice" }
      ]
    }
  }
}