jsonschema_valid/lib.rs
1//! # jsonschema-valid
2//!
3//! A simple crate to perform [JSON Schema](https://json-schema.org/) validation.
4//!
5//! Supports JSON Schema drafts 4, 6, and 7.
6//!
7//! ## Example:
8//!
9//! The following example validates some JSON data against a draft 6 JSON schema.
10//!
11//! ```rust
12//! # fn main() -> Result<(), Box<dyn std::error::Error + 'static>> {
13//! # use serde_json::Value;
14//! # use jsonschema_valid::schemas;
15//! # let schema_json = "{}";
16//! # let your_json_data = "{}";
17//! let schema: Value = serde_json::from_str(schema_json)?;
18//! let data: Value = serde_json::from_str(your_json_data)?;
19//! let cfg = jsonschema_valid::Config::from_schema(&schema, Some(schemas::Draft::Draft6))?;
20//! // Validate the schema itself
21//! assert!(cfg.validate_schema().is_ok());
22//! // Validate a JSON instance against the schema
23//! assert!(cfg.validate(&data).is_ok());
24//!
25//! # Ok(()) }
26//! ````
27
28#![warn(missing_docs)]
29
30use serde_json::Value;
31
32mod config;
33mod context;
34mod error;
35mod format;
36mod resolver;
37pub mod schemas;
38mod unique;
39mod util;
40mod validators;
41
42pub use crate::config::Config;
43use crate::context::Context;
44pub use crate::error::{ErrorIterator, ValidationError};
45
46/// Validates a given JSON instance against a given JSON schema, returning the
47/// errors, if any. draft may provide the schema draft to use. If not provided,
48/// it will be determined automatically from the schema.
49///
50/// # Arguments
51///
52/// * `cfg`: The configuration object to use
53/// * `instance`: The JSON document to validate
54///
55/// # Returns
56///
57/// * `errors`: A `Result` indicating whether there were any validation errors.
58/// If `Ok(())`, the `instance` is valid against `schema`. If `Err(e)`, `e` is
59/// an iterator over all of the validation errors.
60///
61/// ## Example:
62///
63/// The following example validates some JSON data against a draft 6 JSON schema.
64///
65/// ```rust
66/// # fn main() -> Result<(), Box<dyn std::error::Error + 'static>> {
67/// # use serde_json::Value;
68/// # use jsonschema_valid::{schemas, Config};
69/// # let schema_json = "{\"type\": \"integer\"}";
70/// # let your_json_data = "\"string\"";
71/// let schema: Value = serde_json::from_str(schema_json)?;
72/// let data: Value = serde_json::from_str(your_json_data)?;
73/// let cfg = jsonschema_valid::Config::from_schema(&schema, Some(schemas::Draft::Draft6))?;
74///
75/// let mut validation = jsonschema_valid::validate(&cfg, &data);
76/// if let Err(errors) = validation {
77/// for error in errors {
78/// println!("Error: {}", error);
79/// }
80/// }
81///
82/// # Ok(()) }
83/// ````
84pub fn validate<'a>(
85 cfg: &'a config::Config<'a>,
86 instance: &'a Value,
87) -> Result<(), ErrorIterator<'a>> {
88 let mut errors = validators::descend(
89 cfg,
90 instance,
91 cfg.get_schema(),
92 None,
93 Context::new_from(cfg.get_schema()),
94 )
95 .peekable();
96
97 if errors.peek().is_none() {
98 Ok(())
99 } else {
100 Err(Box::new(errors))
101 }
102}
103
104#[cfg(test)]
105mod tests {
106 use super::*;
107
108 use std::fs;
109 use std::path::PathBuf;
110
111 // Test files we know will fail.
112 const KNOWN_FAILURES: &[&str] = &["refRemote.json"];
113
114 fn test_draft(dirname: &str, draft: schemas::Draft) {
115 let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
116 path.push("JSON-Schema-Test-Suite/tests");
117 path.push(dirname);
118
119 let paths = fs::read_dir(path).unwrap();
120
121 for entry in paths {
122 let dir_entry = &entry.unwrap();
123 if KNOWN_FAILURES.contains(&dir_entry.file_name().to_str().unwrap()) {
124 continue;
125 }
126
127 let path = dir_entry.path();
128 if path.extension().map_or_else(|| "", |x| x.to_str().unwrap()) == "json" {
129 println!("Testing {:?}", path.display());
130 let file = fs::File::open(path).unwrap();
131 let json: Value = serde_json::from_reader(file).unwrap();
132 for testset in json.as_array().unwrap().iter() {
133 println!(
134 " Test set {}",
135 testset.get("description").unwrap().as_str().unwrap()
136 );
137 let schema = testset.get("schema").unwrap();
138 let tests = testset.get("tests").unwrap();
139 for test in tests.as_array().unwrap().iter() {
140 println!(
141 " Test {}",
142 test.get("description").unwrap().as_str().unwrap()
143 );
144 let data = test.get("data").unwrap();
145 let valid = test.get("valid").unwrap();
146 if let Value::Bool(expected_valid) = valid {
147 let cfg = config::Config::from_schema(schema, Some(draft)).unwrap();
148 assert!(cfg.validate_schema().is_ok());
149 let result = validate(&cfg, data);
150 assert_eq!(result.is_ok(), *expected_valid);
151 let cfg2 = config::Config::from_schema(schema, Some(draft)).unwrap();
152 let result2 = cfg2.validate(data);
153 assert!(cfg2.validate_schema().is_ok());
154 assert_eq!(result2.is_ok(), *expected_valid);
155 }
156 }
157 }
158 }
159 }
160 }
161
162 #[test]
163 fn test_draft7() {
164 test_draft("draft7", schemas::Draft::Draft7);
165 }
166
167 #[test]
168 fn test_draft6() {
169 test_draft("draft6", schemas::Draft::Draft6);
170 }
171
172 #[test]
173 fn test_draft4() {
174 test_draft("draft4", schemas::Draft::Draft4);
175 }
176}