rsonschema 0.1.16

A fast, simple, user-friendly JSON Schema validator for Rust
Documentation

rsonschema

Crates.io docs.rs PyPI CI License

A fast, simple, and user-friendly JSON Schema validator for Rust, with Python bindings.

Prologue

In the world of data validation, ensuring your data conforms to a specified structure is crucial.

At hiop, we sought a language-agnostic format to define how data should be structured, and JSON Schema stood out as the perfect solution.

This inspired the creation of rsonschema, a fast, simple, and user-friendly JSON Schema validator for Rust.

Why Rust?

Rust is celebrated for its performance and safety capabilities. These attributes make it an excellent choice for building a fast, user-friendly, secure, and efficient validator.

Alternatives

  • jsonschema: was previously our choice, offering robust validation but suffering from complex error handling. For example:

    1. jsonschema::error::ValidationError borrows the instance attribute, adding complexity.
    2. it lacks useful error messages for end users, especially when validating schemas with Schema Composition failures.
  • valico: like jsonschema, it has complex error handling. Moreover it is not actively maintained.

  • schemars: a de facto standard for schema generation with over 19 million downloads. However, it lacks validation APIs.

Usage

Rust

Add rsonschema to your Cargo.toml:

cargo add rsonschema

Here's how you can start using rsonschema in your Rust project:

let schema = serde_json::json!({
    "$schema": "https://json-schema.org/draft/2020-12/schema",
    "minLength": 3
});

let instance = serde_json::json!("foo");
let report = rsonschema::validate(
    &instance,
    schema.clone(),
);
assert!(report.is_valid());

let instance = serde_json::json!("a");
let report = rsonschema::validate(
    &instance,
    schema,
);
assert_eq!(
    report,
    rsonschema::ValidationReport {
        errors: Some(
            rsonschema::error::ValidationErrors::from([
                rsonschema::error::ValidationError {
                    instance: serde_json::json!("a"),
                    type_: rsonschema::error::type_::ValidationErrorType::MinLength {
                        limit: 3.into(),
                    },
                    ..Default::default()
                }
            ])
        ),
        ..Default::default()
    }
);

Python

Install from PyPI (requires Python >= 3.10):

pip install rsonschema
import rsonschema

schema = {"$schema": "https://json-schema.org/draft/2020-12/schema", "minLength": 3}

# validate(instance, schema, pointer=None, ref_resolver=None)
errors = rsonschema.validate("foo", schema, None, None)
assert errors == []

errors = rsonschema.validate("a", schema, None, None)
assert len(errors) == 1
assert str(errors[0])  # human-readable error description

Error Messages

One of rsonschema's key strengths is the quality of its human-readable error messages. Each error includes the failing value, the full path to it within the document, and a precise description — making them suitable to display directly to end users.

Simple constraint violation

let schema = serde_json::json!({
    "$schema": "https://json-schema.org/draft/2020-12/schema",
    "type": "string",
    "minLength": 5
});

let report = rsonschema::validate(&serde_json::json!("hi"), schema);
let error = report.errors.unwrap().into_iter().min().unwrap();
println!("{error}");
// "hi": must be longer than `5` characters

Nested objects

The pointer tracks the full path from the document root to the failing value:

let schema = serde_json::json!({
    "$schema": "https://json-schema.org/draft/2020-12/schema",
    "properties": {
        "user": {
            "required": ["name", "email"]
        }
    }
});

let report = rsonschema::validate(
    &serde_json::json!({"user": {"name": "Alice"}}),
    schema,
);
let error = report.errors.unwrap().into_iter().min().unwrap();
println!("{error}");
// {"name":"Alice"} at `user`: missing required: `email`

Schema composition (anyOf, oneOf, allOf)

When validation fails on a composition keyword, rsonschema surfaces the most relevant inner error rather than a generic "did not match any schema" message. Relevance is determined by how closely the instance resembles each branch, using string similarity on values and property names.

let schema = serde_json::json!({
    "$schema": "https://json-schema.org/draft/2020-12/schema",
    "anyOf": [
        {"type": "string", "minLength": 5},
        {"type": "integer", "minimum": 10}
    ]
});

let report = rsonschema::validate(&serde_json::json!("hi"), schema);
let error = report.errors.unwrap().into_iter().min().unwrap();
println!("{error}");
// "hi": must be longer than `5` characters

"hi" is clearly closer to the string branch, so the minLength error from that branch is surfaced instead of a generic composition failure.

Python bindings

The same messages are available via the .message attribute on each error object:

import rsonschema

schema = {
    "$schema": "https://json-schema.org/draft/2020-12/schema",
    "properties": {
        "user": {"required": ["name", "email"]}
    },
}
errors = rsonschema.validate({"user": {"name": "Alice"}}, schema, None, None)
print(str(errors[0]))
# {"name":"Alice"} at `user`: missing required: `email`

Performance

rsonschema is benchmarked against jsonschema across representative scenarios. Selected results on Apple M3 (lower is better):

Scenario rsonschema jsonschema (cold)
Simple string validation 738 ns 2.14 µs
Complex object (5 fields) 6.85 µs 8.95 µs
Array of 50 objects 54.0 µs 7.74 µs
anyOf composition 3.25 µs 4.91 µs

Cold means the competitor also compiles the schema on every call, matching rsonschema's usage model. See BENCHMARKS.md for the full methodology and results, including Python bindings.

Scope

rsonschema targets a specific, well-defined subset of JSON Schema:

  • Draft: only the latest (2020-12) specification is supported. Older drafts are not.
  • Validation only: the library validates instances against schemas and reports errors — it does not generate schemas or produce annotation output.
  • All standard keywords are implemented, including schema composition (allOf, anyOf, oneOf, not), conditionals (if/then/else), references ($ref, $anchor), unevaluated keywords (unevaluatedProperties, unevaluatedItems), and format assertions.
  • Intentionally unsupported: dynamic keywords $dynamicAnchor and $dynamicRef are excluded because they introduce significant complexity with limited practical benefit.

All official JSON Schema Test Suite tests, located in the tests folder, pass — except for the unsupported dynamic keywords above.

Community

Contribution

We firmly believe that collaboration is the key to innovation!

If you find a bug or have a feature request, please open an issue. If you want to go further and tackle it, open a pull request on our GitHub repository.

See CONTRIBUTING.md for development guidelines.

License

rsonschema is licensed under the Apache-2.0 License. See the LICENSE file for more details.