JSON Structure is a data structure definition language that enforces strict typing, modularity, and determinism.
A Rust implementation of the JSON Structure schema validation library.
Add to your Cargo.toml:
[dependencies]
json-structure = "0.1"
use json_structure::{SchemaValidator, InstanceValidator};
fn main() {
// Define a schema
let schema_json = r#"{
"$id": "https://example.com/person",
"name": "Person",
"type": "object",
"properties": {
"name": { "type": "string" },
"age": { "type": "int32" },
"email": { "type": "string" }
},
"required": ["name", "email"]
}"#;
// Validate the schema
let schema_validator = SchemaValidator::new();
let schema_result = schema_validator.validate(schema_json);
if !schema_result.is_valid() {
for error in schema_result.errors() {
eprintln!("Schema error: {}", error);
}
return;
}
// Parse schema for instance validation
let schema: serde_json::Value = serde_json::from_str(schema_json).unwrap();
// Validate an instance
let instance_json = r#"{
"name": "Alice",
"age": 30,
"email": "alice@example.com"
}"#;
let instance_validator = InstanceValidator::new();
let instance_result = instance_validator.validate(instance_json, &schema);
if instance_result.is_valid() {
println!("Instance is valid!");
} else {
for error in instance_result.errors() {
eprintln!("Validation error: {}", error);
}
}
}
The SchemaValidator validates JSON Structure schema documents:
use json_structure::{SchemaValidator, SchemaValidatorOptions};
// With default options
let validator = SchemaValidator::new();
// With custom options
let options = SchemaValidatorOptions {
allow_import: true,
max_validation_depth: 64,
warn_on_unused_extension_keywords: true,
..Default::default()
};
let validator = SchemaValidator::with_options(options);
let result = validator.validate(schema_json);
The InstanceValidator validates JSON instances against schemas:
use json_structure::{InstanceValidator, InstanceValidatorOptions};
// With default options
let validator = InstanceValidator::new();
// With extended validation (enables constraint keywords)
let options = InstanceValidatorOptions {
extended: true,
allow_import: false,
};
let validator = InstanceValidator::with_options(options);
let result = validator.validate(instance_json, &schema);
Errors include line and column information:
for error in result.errors() {
println!(
"[{}:{}] {}: {}",
error.location.line,
error.location.column,
error.code,
error.message
);
}
| Type | Description |
|---|---|
string |
UTF-8 string |
boolean |
true or false |
null |
Null value |
number |
Any JSON number |
integer |
Alias for int32 |
int8 - int128 |
Signed integers |
uint8 - uint128 |
Unsigned integers |
float, double, decimal |
Floating-point numbers |
date |
Date (YYYY-MM-DD) |
time |
Time (HH:MM:SS) |
datetime |
RFC 3339 datetime |
duration |
ISO 8601 duration |
uuid |
UUID string |
uri |
URI string |
binary |
Base64-encoded bytes |
jsonpointer |
JSON Pointer |
| Type | Description | Required Keywords |
|---|---|---|
object |
Typed properties | properties |
array |
Homogeneous list | items |
set |
Unique list | items |
map |
String-keyed dictionary | values |
tuple |
Fixed-length array | properties + tuple |
choice |
Discriminated union | choices + selector |
any |
Any value | (none) |
Enable extensions using $uses in your schema:
{
"$id": "https://example.com/schema",
"$uses": ["JSONStructureValidation", "JSONStructureConditionalComposition"],
"name": "ValidatedSchema",
"type": "string",
"minLength": 1,
"maxLength": 100
}
minLength, maxLength, pattern, minimum, maximum, etc.)allOf, anyOf, oneOf, not, if/then/else)$import, $importdefs)altnames)unit)use json_structure::{ValidationResult, Severity};
fn process_result(result: &ValidationResult) {
// Check if validation passed
if result.is_valid() {
println!("Valid!");
return;
}
// Check for errors or warnings
if result.has_errors() {
for error in result.errors() {
println!("[{}] {} at {}", error.code(), error.message(), error.path());
}
}
if result.has_warnings() {
for warning in result.warnings() {
println!("Warning: {}", warning.message());
}
}
// Get counts
println!("Errors: {}, Warnings: {}", result.error_count(), result.warning_count());
}
Both validators implement Default:
use json_structure::{SchemaValidator, InstanceValidator};
let schema_validator = SchemaValidator::default();
let instance_validator = InstanceValidator::default();
ValidationError implements std::error::Error for integration with Rust’s error handling:
use json_structure::ValidationError;
fn validate_something() -> Result<(), Box<dyn std::error::Error>> {
let validator = json_structure::SchemaValidator::new();
let result = validator.validate("{}");
if let Some(error) = result.errors().next() {
return Err(error.clone().into());
}
Ok(())
}
MIT License - see LICENSE for details.
The SDK includes jstruct, a CLI tool for validating schemas and instances.
cargo install json-structure --features cli
Or build from source:
cargo build --release --features cli
Validate one or more JSON Structure schema files:
# Check a single schema
jstruct check schema.struct.json
# Check multiple schemas
jstruct check schema1.json schema2.json
# Use quiet mode (no output, just exit code)
jstruct check -q schema.json
# Output as JSON
jstruct check --format json schema.json
# Output as TAP (Test Anything Protocol)
jstruct check --format tap schema.json
Validate JSON instances against a schema:
# Validate instance against schema
jstruct validate --schema schema.json data.json
# Validate multiple instances
jstruct validate -s schema.json data1.json data2.json
# With extended validation (constraint keywords)
jstruct validate --extended -s schema.json data.json
# Output as JSON
jstruct validate -s schema.json --format json data.json
| Code | Meaning |
|---|---|
| 0 | All files valid |
| 1 | One or more files invalid |
| 2 | Error (file not found, etc.) |
Text (default):
✓ schema.json: valid
✗ bad-schema.json: invalid
- /$id: Missing required property "$id"
JSON:
[{"file":"schema.json","valid":true,"errors":[]}]
TAP:
1..2
ok 1 - schema.json
not ok 2 - bad-schema.json
- /$id: Missing required property "$id"