jsonschema-rs
A high-performance JSON Schema validator for Python
Description
jsonschema-rs
<img alt="Supported Dialects" src="https://img.shields.io/endpoint?url=https%3A%2F%2Fbowtie.report%2Fbadges%2Frust-jsonschema%2Fsupported_versions.json&style=flat-square">
A high-performance JSON Schema validator for Python.
import jsonschema_rs
schema = {"maxLength": 5}
instance = "foo"
# One-off validation
try:
jsonschema_rs.validate(schema, "incorrect")
except jsonschema_rs.ValidationError as exc:
assert str(exc) == '''"incorrect" is longer than 5 characters
Failed validating "maxLength" in schema
On instance:
"incorrect"'''
# Build & reuse (faster)
validator = jsonschema_rs.validator_for(schema)
# Iterate over errors
for error in validator.iter_errors(instance):
print(f"Error: {error}")
print(f"Location: {error.instance_path}")
# Boolean result
assert validator.is_valid(instance)
# Structured output (JSON Schema Output v1)
evaluation = validator.evaluate(instance)
for error in evaluation.errors():
print(f"Error at {error['instanceLocation']}: {error['error']}")
⚠️ Upgrading from older versions? Check our Migration Guide for key changes.
Migrating from
jsonschema? See the jsonschema migration guide.
Highlights
- 📚 Full support for popular JSON Schema drafts
- 🌐 Remote reference fetching (network/file)
- 🔧 Custom keywords and format validators
- ✨ Meta-schema validation for schema documents
Supported drafts
The following drafts are supported:
You can check the current status on the Bowtie Report.
Installation
To install jsonschema-rs via pip run the following command:
pip install jsonschema-rs
Usage
If you have a schema as a JSON string, then you could pass it to validator_for
to avoid parsing on the Python side:
import jsonschema_rs
validator = jsonschema_rs.validator_for('{"minimum": 42}')
...
You can use draft-specific validators for different JSON Schema versions:
import jsonschema_rs
# Automatic draft detection
validator = jsonschema_rs.validator_for({"minimum": 42})
# Draft-specific validators
validator = jsonschema_rs.Draft7Validator({"minimum": 42})
validator = jsonschema_rs.Draft201909Validator({"minimum": 42})
validator = jsonschema_rs.Draft202012Validator({"minimum": 42})
JSON Schema allows for format validation through the format keyword. While jsonschema-rs
provides built-in validators for standard formats, you can also define custom format validators
for domain-specific string formats.
To implement a custom format validator:
- Define a function that takes a
strand returns abool. - Pass it with the
formatsargument. - Ensure validate_formats is set appropriately (especially for Draft 2019-09 and 2020-12).
import jsonschema_rs
def is_currency(value):
# The input value is always a string
return len(value) == 3 and value.isascii()
validator = jsonschema_rs.validator_for(
{"type": "string", "format": "currency"},
formats={"currency": is_currency},
validate_formats=True # Important for Draft 2019-09 and 2020-12
)
validator.is_valid("USD") # True
validator.is_valid("invalid") # False
Custom Keywords
You can extend JSON Schema with custom keywords for domain-specific validation rules. Custom keywords are classes that receive the keyword value during schema compilation and validate instances at runtime:
import jsonschema_rs
class DivisibleBy:
def __init__(self, parent_schema, value, schema_path):
self.divisor = value
def validate(self, instance):
if isinstance(instance, int) and instance % self.divisor != 0:
raise ValueError(f"{instance} is not divisible by {self.divisor}")
validator = jsonschema_rs.validator_for(
{"type": "integer", "divisibleBy": 3},
keywords={"divisibleBy": DivisibleBy},
)
validator.is_valid(9) # True
validator.is_valid(10) # False
When validate raises, the original exception is preserved as the __cause__ of the ValidationError, so callers can inspect it:
try:
validator.validate(instance)
except jsonschema_rs.ValidationError as e:
print(type(e.__cause__)) # <class 'ValueError'>
print(e.__cause__) # original message
Additional configuration options are available for fine-tuning the validation process:
validate_formats: Override the draft-specific default behavior for format validation.ignore_unknown_formats: Control whether unrecognized formats should be reported as errors.base_uri- a base URI for all relative$refin the schema.
Example usage of these options:
import jsonschema_rs
validator = jsonschema_rs.Draft202012Validator(
{"type": "string", "format": "date"},
validate_formats=True,
ignore_unknown_formats=False
)
# This will validate the "date" format
validator.is_valid("2023-05-17") # True
validator.is_valid("not a date") # False
# With ignore_unknown_formats=False, using an unknown format will raise an error
invalid_schema = {"type": "string", "format": "unknown"}
try:
jsonschema_rs.Draft202012Validator(
invalid_schema, validate_formats=True, ignore_unknown_formats=False
)
except jsonschema_rs.ValidationError as exc:
assert str(exc) == '''Unknown format: 'unknown'. Adjust configuration to ignore unrecognized formats
Failed validating "format" in schema
On instance:
"unknown"'''
Structured Output with evaluate
When you need more than a boolean result, use the evaluate API to access the JSON Schema Output v1 formats:
import jsonschema_rs
schema = {
"type": "array",
"prefixItems": [{"type": "string"}],
"items": {"type": "integer"},
}
evaluation = jsonschema_rs.evaluate(schema, ["hello", "oops"])
assert evaluation.flag() == {"valid": False}
assert evaluation.list() == {
"valid": False,
"details": [
{
"evaluationPath": "",
"instanceLocation": "",
"schemaLocation": "",
"valid": False,
},
{
"valid": True,
"evaluationPath": "/type",
"instanceLocation": "",
"schemaLocation": "/type",
},
{
"valid": False,
"evaluationPath": "/items",
"instanceLocation": "",
"schemaLocation": "/items",
"droppedAnnotations": True,
},
{
"valid": False,
"evaluationPath": "/items",
"instanceLocation": "/1",
"schemaLocation": "/items",
},
{
"valid": False,
"evaluationPath": "/items/type",
"instanceLocation": "/1",
"schemaLocation": "/items/type",
"errors": {"type": '"oops" is not of type "integer"'},
},
{
"valid": True,
"evaluationPath": "/prefixItems",
"instanceLocation": "",
"schemaLocation": "/prefixItems",
"annotations": 0,
},
{
"valid": True,
"evaluationPath": "/prefixItems/0",
"instanceLocation": "/0",
"schemaLocation": "/prefixItems/0",
},
{
"valid": True,
"evaluationPath": "/prefixItems/0/type",
"instanceLocation": "/0",
"schemaLocation": "/prefixItems/0/type",
},
],
}
hierarchical = evaluation.hierarchical()
assert hierarchical == {
"valid": False,
"evaluationPath": "",
"instanceLocation": "",
"schemaLocation": "",
"details": [
{
"valid": True,
"evaluationPath": "/type",
"instanceLocation": "",
"schemaLocation": "/type",
},
{
"valid": False,
"evaluationPath": "/items",
"instanceLocation": "",
"schemaLocation": "/items",
"droppedAnnotations": True,
"details": [
{
"valid": False,
"evaluationPath": "/items",
"instanceLocation": "/1",
"schem