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