Fast JSON schema for Python

Installation

pip install fastjsonschema

Support only for Python 3.3 and higher.

About

fastjsonschema implements validation of JSON documents by JSON schema. The library implements JSON schema drafts 04, 06, and 07. The main purpose is to have a really fast implementation. See some numbers:

  • Probably the most popular, jsonschema, can take up to 5 seconds for valid inputs and 1.2 seconds for invalid inputs.

  • Second most popular, json-spec, is even worse with up to 7.2 and 1.7 seconds.

  • Last validictory, now deprecated, is much better with 370 or 23 milliseconds, but it does not follow all standards, and it can be still slow for some purposes.

With this library you can gain big improvements as fastjsonschema takes only about 25 milliseconds for valid inputs and 2 milliseconds for invalid ones. Pretty amazing, right? :-)

Technically it works by generating the most stupid code on the fly, which is fast but is hard to write by hand. The best efficiency is achieved when a validator is compiled once and used many times, of course. It works similarly like regular expressions. But you can also generate the code to a file, which is even slightly faster.

You can run the performance benchmarks on your computer or server with the included script:

$ make performance
fast_compiled                  valid      ==>  0.0993900
fast_compiled                  invalid    ==>  0.0041089
fast_compiled_without_exc      valid      ==>  0.0465258
fast_compiled_without_exc      invalid    ==>  0.0023688
fast_file                      valid      ==>  0.0989483
fast_file                      invalid    ==>  0.0041104
fast_not_compiled              valid      ==> 11.9572681
fast_not_compiled              invalid    ==>  2.9512092
jsonschema                     valid      ==>  5.2233240
jsonschema                     invalid    ==>  1.3227916
jsonschema_compiled            valid      ==>  0.4447982
jsonschema_compiled            invalid    ==>  0.0231333
jsonspec                       valid      ==>  4.1450569
jsonspec                       invalid    ==>  1.0485777
validictory                    valid      ==>  0.2730411
validictory                    invalid    ==>  0.0183669

This library follows and implements JSON schema draft-04, draft-06, and draft-07. Sometimes it’s not perfectly clear, so I recommend also check out this understanding JSON schema.

Note that there are some differences compared to JSON schema standard:

  • Regular expressions are full Python ones, not only what JSON schema allows. It’s easier to allow everything, and also it’s faster to compile without limits. So keep in mind that when you will use a more advanced regular expression, it may not work with other libraries or in other languages.

  • Because Python matches new line for a dollar in regular expressions (a$ matches a and a\\n), instead of $ is used \Z and all dollars in your regular expression are changed to \\Z as well. When you want to use dollar as regular character, you have to escape it (\$).

  • JSON schema says you can use keyword default for providing default values. This implementation uses that and always returns transformed input data.

Usage

import fastjsonschema

point_schema = {
    "type": "object",
    "properties": {
        "x": {
            "type": "number",
        },
        "y": {
            "type": "number",
        },
    },
    "required": ["x", "y"],
    "additionalProperties": False,
}

point_validator = fastjsonschema.compile(point_schema)
try:
    point_validator({"x": 1.0, "y": 2.0})
except fastjsonschema.JsonSchemaException as e:
    print(f"Data failed validation: {e}")

API

exception fastjsonschema.JsonSchemaDefinitionException[source]

Exception raised by generator of validation function.

exception fastjsonschema.JsonSchemaException[source]

Base exception of fastjsonschema library.

exception fastjsonschema.JsonSchemaValueException(message, value=None, name=None, definition=None, rule=None)[source]

Exception raised by validation function. Available properties:

  • message containing human-readable information what is wrong (e.g. data.property[index] must be smaller than or equal to 42),

  • invalid value (e.g. 60),

  • name of a path in the data structure (e.g. data.property[index]),

  • path as an array in the data structure (e.g. ['data', 'property', 'index']),

  • the whole definition which the value has to fulfil (e.g. {'type': 'number', 'maximum': 42}),

  • rule which the value is breaking (e.g. maximum)

  • and rule_definition (e.g. 42).

Changed in version 2.14.0: Added all extra properties.

exception fastjsonschema.JsonSchemaValuesException(errors)[source]

Exception raised by validation function. It is a collection of all errors.

fastjsonschema.compile(definition: dict | bool, handlers: dict = {}, formats: dict = {}, use_default: bool = True, use_formats: bool = True, detailed_exceptions: bool = True, fast_fail: bool = True)[source]

Generates validation function for validating JSON schema passed in definition. Example:

import fastjsonschema

validate = fastjsonschema.compile({'type': 'string'})
validate('hello')

This implementation supports keyword default (can be turned off by passing use_default=False):

validate = fastjsonschema.compile({
    'type': 'object',
    'properties': {
        'a': {'type': 'number', 'default': 42},
    },
})

data = validate({})
assert data == {'a': 42}

Supported implementations are draft-04, draft-06 and draft-07. Which version should be used is determined by $draft in your definition. When not specified, the latest implementation is used (draft-07).

validate = fastjsonschema.compile({
    '$schema': 'http://json-schema.org/draft-04/schema',
    'type': 'number',
})

You can pass mapping from URI scheme to function that should be used to retrieve remote references used in your definition in parameter handlers. When no handler is registered for a scheme, the URI is fetched automatically via urllib (for example http, https, or file URLs).

Warning

Do not compile or validate untrusted schemas without custom handlers. A schema containing $ref can trigger outbound HTTP requests to arbitrary URLs, including internal or loopback addresses (server-side request forgery). Provide handlers to restrict which URIs are resolved, or pre-resolve references before passing the schema to this library.

def http_handler(uri):
    if not uri.startswith('https://schemas.example.com/'):
        raise ValueError('ref not allowed')
    import urllib.request
    with urllib.request.urlopen(uri) as response:
        return json.loads(response.read())

validate = fastjsonschema.compile(definition, handlers={
    'http': http_handler,
    'https': http_handler,
})

Also, you can pass mapping for custom formats. Key is the name of your formatter and value can be regular expression, which will be compiled or callback returning bool (or you can raise your own exception).

validate = fastjsonschema.compile(definition, formats={
    'foo': r'foo|bar',
    'bar': lambda value: value in ('foo', 'bar'),
})

Note that formats are automatically used as assertions. It can be turned off by passing use_formats=False. When disabled, custom formats are disabled as well. (Added in 2.19.0.)

If you don’t need detailed exceptions, you can turn the details off and gain additional performance by passing detailed_exceptions=False.

By default, the execution stops with the first validation error. If you need to collect all the errors, turn this off by passing fast_fail=False.

Exception JsonSchemaDefinitionException is raised when generating the code fails (bad definition).

Exception JsonSchemaValueException is raised from generated function when validation fails (data do not follow the definition).

Exception JsonSchemaValuesException is raised from generated function when validation fails (data do not follow the definition) contatining all the errors (when fast_fail is set to False).

fastjsonschema.compile_to_code(definition: dict | bool, handlers: dict = {}, formats: dict = {}, use_default: bool = True, use_formats: bool = True, detailed_exceptions: bool = True, fast_fail: bool = True)[source]

Generates validation code for validating JSON schema passed in definition. Example:

import fastjsonschema

code = fastjsonschema.compile_to_code({'type': 'string'})
with open('your_file.py', 'w') as f:
    f.write(code)

You can also use it as a script:

echo "{'type': 'string'}" | python3 -m fastjsonschema > your_file.py
python3 -m fastjsonschema "{'type': 'string'}" > your_file.py

Exception JsonSchemaDefinitionException is raised when generating the code fails (bad definition).

Remote $ref URIs are resolved the same way as in compile; see its documentation for handlers and security considerations.

fastjsonschema.validate(definition: dict | bool, data, handlers: dict = {}, formats: dict = {}, use_default: bool = True, use_formats: bool = True, detailed_exceptions: bool = True, fast_fail: bool = True)[source]

Validation function for lazy programmers or for use cases when you need to call validation only once, so you do not have to compile it first. Use it only when you do not care about performance (even though it will be still faster than alternative implementations).

import fastjsonschema

fastjsonschema.validate({'type': 'string'}, 'hello')
# same as: compile({'type': 'string'})('hello')

Preferred is to use compile function.

The handlers parameter controls resolution of remote $ref URIs; see compile for details and security considerations when schemas are not fully trusted.