# rsonschema
[](https://crates.io/crates/rsonschema)
[](https://docs.rs/rsonschema)
[](https://pypi.org/project/rsonschema/)
[](https://github.com/hiop-oss/rsonschema/actions/workflows/validate.yml)
[](https://github.com/hiop-oss/rsonschema/blob/master/LICENSE)
A fast, simple, and user-friendly
[JSON Schema](https://json-schema.org/) 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](https://hiop.io),
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](https://docs.rs/jsonschema/latest/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](https://json-schema.org/understanding-json-schema/reference/combining)
failures.
- **[valico](https://docs.rs/valico/latest/valico/)**:
like `jsonschema`,
it has complex error handling.
Moreover it is not actively maintained.
- **[schemars](https://docs.rs/schemars/latest/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`:
```sh
cargo add rsonschema
```
Here's how you can start using `rsonschema` in your Rust project:
```rust
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):
```sh
pip install rsonschema
```
```python
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
```rust
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:
```rust
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.
```rust
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:
```python
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`](https://docs.rs/jsonschema) across representative scenarios.
Selected results on Apple M3 (lower is better):
| 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](./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`](https://json-schema.org/draft/2020-12/release-notes))
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`](https://www.learnjsonschema.com/2020-12/core/dynamicanchor/)
and
[`$dynamicRef`](https://www.learnjsonschema.com/2020-12/core/dynamicref/)
are excluded because they introduce significant complexity
with limited practical benefit.
All [official JSON Schema Test Suite][json-schema-test-suite] tests,
located in the [`tests`][rsonschema-tests] folder, pass — except for the
unsupported dynamic keywords above.
[json-schema-test-suite]: https://github.com/json-schema-org/JSON-Schema-Test-Suite
[rsonschema-tests]: https://github.com/hiop-oss/rsonschema/tree/master/rust/tests
## 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](https://github.com/hiop-oss/rsonschema).
See [CONTRIBUTING.md](./CONTRIBUTING.md) for development guidelines.
### License
`rsonschema` is licensed under the Apache-2.0 License.
See the [LICENSE](./LICENSE.md) file for more details.