Expand description
A high-performance JSON Schema validator for Rust.
- š Support for popular JSON Schema drafts
- š§ Custom keywords and format validators
- š Remote reference fetching (network/file)
- šØ
Basic
output style as per JSON Schema spec - š WebAssembly support
Ā§Supported drafts
Compliance levels vary across drafts, with newer versions having some unimplemented keywords.
Ā§Validation
The jsonschema
crate offers two main approaches to validation: one-off validation and reusable validators.
For simple use cases where you need to validate an instance against a schema once, use is_valid
or validate
functions:
use serde_json::json;
let schema = json!({"type": "string"});
let instance = json!("Hello, world!");
assert!(jsonschema::is_valid(&schema, &instance));
assert!(jsonschema::validate(&schema, &instance).is_ok());
For better performance, especially when validating multiple instances against the same schema, build a validator once and reuse it:
use serde_json::json;
let schema = json!({"type": "string"});
let validator = jsonschema::validator_for(&schema)?;
assert!(validator.is_valid(&json!("Hello, world!")));
assert!(!validator.is_valid(&json!(42)));
assert!(validator.validate(&json!(42)).is_err());
// Iterate over all errors
let instance = json!(42);
for error in validator.iter_errors(&instance) {
eprintln!("Error: {}", error);
eprintln!("Location: {}", error.instance_path);
}
Ā§Configuration
jsonschema
provides several ways to configure and use JSON Schema validation.
Ā§Draft-specific Modules
The library offers modules for specific JSON Schema draft versions:
Each module provides:
- A
new
function to create a validator - An
is_valid
function for validation with a boolean result - An
validate
function for getting the first validation error - An
options
function to create a draft-specific configuration builder
Hereās how you can explicitly use a specific draft version:
use serde_json::json;
let schema = json!({"type": "string"});
let validator = jsonschema::draft7::new(&schema)?;
assert!(validator.is_valid(&json!("Hello")));
assert!(validator.validate(&json!("Hello")).is_ok());
You can also use the convenience is_valid
and validate
functions:
use serde_json::json;
let schema = json!({"type": "number", "minimum": 0});
let instance = json!(42);
assert!(jsonschema::draft202012::is_valid(&schema, &instance));
assert!(jsonschema::draft202012::validate(&schema, &instance).is_ok());
For more advanced configuration, you can use the draft-specific options
function:
use serde_json::json;
let schema = json!({"type": "string", "format": "ends-with-42"});
let validator = jsonschema::draft202012::options()
.with_format("ends-with-42", |s| s.ends_with("42"))
.should_validate_formats(true)
.build(&schema)?;
assert!(validator.is_valid(&json!("Hello 42")));
assert!(!validator.is_valid(&json!("No!")));
Ā§General Configuration
For configuration options that are not draft-specific, jsonschema
provides a builder via jsonschema::options()
.
Hereās an example:
use serde_json::json;
let schema = json!({"type": "string"});
let validator = jsonschema::options()
// Add configuration options here
.build(&schema)?;
assert!(validator.is_valid(&json!("Hello")));
For a complete list of configuration options and their usage, please refer to the ValidationOptions
struct.
Ā§Automatic Draft Detection
If you donāt need to specify a particular draft version, you can use jsonschema::validator_for
which automatically detects the appropriate draft:
use serde_json::json;
let schema = json!({"$schema": "http://json-schema.org/draft-07/schema#", "type": "string"});
let validator = jsonschema::validator_for(&schema)?;
assert!(validator.is_valid(&json!("Hello")));
Ā§External References
By default, jsonschema
resolves HTTP references using reqwest
and file references from the local file system.
To enable HTTPS support, add the rustls-tls
feature to reqwest
in your Cargo.toml
:
reqwest = { version = "*", features = ["rustls-tls"] }
You can disable the default behavior using crate features:
- Disable HTTP resolving:
default-features = false, features = ["resolve-file"]
- Disable file resolving:
default-features = false, features = ["resolve-http"]
- Disable both:
default-features = false
You can implement a custom retriever to handle external references. Hereās an example that uses a static map of schemas:
use std::{collections::HashMap, sync::Arc};
use jsonschema::{Retrieve, Uri};
use serde_json::{json, Value};
struct InMemoryRetriever {
schemas: HashMap<String, Value>,
}
impl Retrieve for InMemoryRetriever {
fn retrieve(
&self,
uri: &Uri<&str>,
) -> Result<Value, Box<dyn std::error::Error + Send + Sync>> {
self.schemas
.get(uri.as_str())
.cloned()
.ok_or_else(|| format!("Schema not found: {uri}").into())
}
}
let mut schemas = HashMap::new();
schemas.insert(
"https://example.com/person.json".to_string(),
json!({
"type": "object",
"properties": {
"name": { "type": "string" },
"age": { "type": "integer" }
},
"required": ["name", "age"]
}),
);
let retriever = InMemoryRetriever { schemas };
let schema = json!({
"$ref": "https://example.com/person.json"
});
let validator = jsonschema::options()
.with_retriever(retriever)
.build(&schema)?;
assert!(validator.is_valid(&json!({
"name": "Alice",
"age": 30
})));
assert!(!validator.is_valid(&json!({
"name": "Bob"
})));
Ā§Output Styles
jsonschema
supports the basic
output style as defined in JSON Schema Draft 2019-09.
This styles allow you to serialize validation results in a standardized format using serde
.
use serde_json::json;
let schema_json = json!({
"title": "string value",
"type": "string"
});
let instance = json!("some string");
let validator = jsonschema::validator_for(&schema_json)?;
let output = validator.apply(&instance).basic();
assert_eq!(
serde_json::to_value(output)?,
json!({
"valid": true,
"annotations": [
{
"keywordLocation": "",
"instanceLocation": "",
"annotations": {
"title": "string value"
}
}
]
})
);
Ā§Custom Keywords
jsonschema
allows you to extend its functionality by implementing custom validation logic through custom keywords.
This feature is particularly useful when you need to validate against domain-specific rules that arenāt covered by the standard JSON Schema keywords.
To implement a custom keyword, you need to:
- Create a struct that implements the
Keyword
trait - Create a factory function or closure that produces instances of your custom keyword
- Register the custom keyword with the
Validator
instance using theValidationOptions::with_keyword
method
Hereās a complete example:
use jsonschema::{
paths::{LazyLocation, Location},
Keyword, ValidationError,
};
use serde_json::{json, Map, Value};
use std::iter::once;
// Step 1: Implement the Keyword trait
struct EvenNumberValidator;
impl Keyword for EvenNumberValidator {
fn validate<'i>(
&self,
instance: &'i Value,
location: &LazyLocation,
) -> Result<(), ValidationError<'i>> {
if let Value::Number(n) = instance {
if n.as_u64().map_or(false, |n| n % 2 == 0) {
Ok(())
} else {
return Err(ValidationError::custom(
Location::new(),
location.into(),
instance,
"Number must be even",
));
}
} else {
Err(ValidationError::custom(
Location::new(),
location.into(),
instance,
"Value must be a number",
))
}
}
fn is_valid(&self, instance: &Value) -> bool {
instance.as_u64().map_or(false, |n| n % 2 == 0)
}
}
// Step 2: Create a factory function
fn even_number_validator_factory<'a>(
_parent: &'a Map<String, Value>,
value: &'a Value,
path: Location,
) -> Result<Box<dyn Keyword>, ValidationError<'a>> {
// You can use the `value` parameter to configure your validator if needed
if value.as_bool() == Some(true) {
Ok(Box::new(EvenNumberValidator))
} else {
Err(ValidationError::custom(
Location::new(),
path,
value,
"The 'even-number' keyword must be set to true",
))
}
}
// Step 3: Use the custom keyword
fn main() -> Result<(), Box<dyn std::error::Error>> {
let schema = json!({"even-number": true, "type": "integer"});
let validator = jsonschema::options()
.with_keyword("even-number", even_number_validator_factory)
.build(&schema)?;
assert!(validator.is_valid(&json!(2)));
assert!(!validator.is_valid(&json!(3)));
assert!(!validator.is_valid(&json!("not a number")));
Ok(())
}
In this example, weāve created a custom even-number
keyword that validates whether a number is even.
The EvenNumberValidator
implements the actual validation logic, while the even_number_validator_factory
creates instances of the validator and allows for additional configuration based on the keywordās value in the schema.
You can also use a closure instead of a factory function for simpler cases:
let schema = json!({"even-number": true, "type": "integer"});
let validator = jsonschema::options()
.with_keyword("even-number", |_, _, _| {
Ok(Box::new(EvenNumberValidator))
})
.build(&schema)?;
Ā§Custom Formats
JSON Schema allows for format validation through the format
keyword. While jsonschema
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 or a closure that takes a
&str
and returns abool
. - Register the function with
jsonschema::options().with_format()
.
use serde_json::json;
// Step 1: Define the custom format validator function
fn ends_with_42(s: &str) -> bool {
s.ends_with("42!")
}
// Step 2: Create a schema using the custom format
let schema = json!({
"type": "string",
"format": "ends-with-42"
});
// Step 3: Build the validator with the custom format
let validator = jsonschema::options()
.with_format("ends-with-42", ends_with_42)
.with_format("ends-with-43", |s| s.ends_with("43!"))
.should_validate_formats(true)
.build(&schema)?;
// Step 4: Validate instances
assert!(validator.is_valid(&json!("Hello42!")));
assert!(!validator.is_valid(&json!("Hello43!")));
assert!(!validator.is_valid(&json!(42))); // Not a string
Ā§Notes on Custom Format Validators
- Custom format validators are only called for string instances.
- Format validation can be disabled globally or per-draft using
ValidationOptions
. Ensure format validation is enabled if youāre using custom formats.
Ā§WebAssembly support
When using jsonschema
in WASM environments, be aware that external references are
not supported by default due to WASM limitations:
- No filesystem access (
resolve-file
feature) - No direct HTTP requests, at least right now (
resolve-http
feature)
To use jsonschema
in WASM, disable default features:
jsonschema = { version = "x.y.z", default-features = false }
For external references in WASM you may want to implement a custom retriever. See the External References section for implementation details.
Re-exportsĀ§
pub use error::ErrorIterator;
pub use error::ValidationError;
pub use output::BasicOutput;
ModulesĀ§
- Functionality specific to JSON Schema Draft 4.
- Functionality specific to JSON Schema Draft 6.
- Functionality specific to JSON Schema Draft 7.
- Functionality specific to JSON Schema Draft 2019-09.
- Functionality specific to JSON Schema Draft 2020-12.
- Error types
- Implementation of json schema output formats specified in https://json-schema.org/draft/2020-12/json-schema-core.html#rfc.section.12.2
- Facilities for working with paths within schemas or validated instances.
- Primitive types for property type validators
StructsĀ§
- A document with a concrete interpretation under a JSON Schema specification.
- A URI.
- Configuration options for JSON Schema validation.
- A compiled JSON Schema validator.
EnumsĀ§
- JSON Schema specification versions.
- Errors that can occur during reference resolution and resource handling.
TraitsĀ§
- Trait that allows implementing custom validation for keywords.
- Trait for retrieving resources from external sources.
FunctionsĀ§
- Validate
instance
againstschema
and get atrue
if the instance is valid andfalse
otherwise. Draft is detected automatically. - Create a builder for configuring JSON Schema validation options.
- Validate
instance
againstschema
and return the first error if any. Draft is detected automatically. - Create a validator for the input schema with automatic draft detection and default options.