1 | [![Build Status](https://secure.travis-ci.org/tdegrunt/jsonschema.svg)](http://travis-ci.org/tdegrunt/jsonschema)
|
2 |
|
3 | # jsonschema
|
4 |
|
5 | [JSON schema](http://json-schema.org/) validator, which is designed to be fast and simple to use. JSON Schema versions through draft-07 are fully supported.
|
6 |
|
7 | ## Contributing & bugs
|
8 |
|
9 | Please fork the repository, make the changes in your fork and include tests. Once you're done making changes, send in a pull request.
|
10 |
|
11 | ### Bug reports
|
12 |
|
13 | Please include a test which shows why the code fails.
|
14 |
|
15 | ## Usage
|
16 |
|
17 | ### Simple
|
18 |
|
19 | Simple object validation using JSON schemas.
|
20 |
|
21 | ```javascript
|
22 | var Validator = require('jsonschema').Validator;
|
23 | var v = new Validator();
|
24 | var instance = 4;
|
25 | var schema = {"type": "number"};
|
26 | console.log(v.validate(instance, schema));
|
27 | ```
|
28 |
|
29 | ### Even simpler
|
30 |
|
31 | ```javascript
|
32 | var validate = require('jsonschema').validate;
|
33 | console.log(validate(4, {"type": "number"}));
|
34 | ```
|
35 |
|
36 | ### Complex example, with split schemas and references
|
37 |
|
38 | ```javascript
|
39 | var Validator = require('jsonschema').Validator;
|
40 | var v = new Validator();
|
41 |
|
42 | // Address, to be embedded on Person
|
43 | var addressSchema = {
|
44 | "id": "/SimpleAddress",
|
45 | "type": "object",
|
46 | "properties": {
|
47 | "lines": {
|
48 | "type": "array",
|
49 | "items": {"type": "string"}
|
50 | },
|
51 | "zip": {"type": "string"},
|
52 | "city": {"type": "string"},
|
53 | "country": {"type": "string"}
|
54 | },
|
55 | "required": ["country"]
|
56 | };
|
57 |
|
58 | // Person
|
59 | var schema = {
|
60 | "id": "/SimplePerson",
|
61 | "type": "object",
|
62 | "properties": {
|
63 | "name": {"type": "string"},
|
64 | "address": {"$ref": "/SimpleAddress"},
|
65 | "votes": {"type": "integer", "minimum": 1}
|
66 | }
|
67 | };
|
68 |
|
69 | var p = {
|
70 | "name": "Barack Obama",
|
71 | "address": {
|
72 | "lines": [ "1600 Pennsylvania Avenue Northwest" ],
|
73 | "zip": "DC 20500",
|
74 | "city": "Washington",
|
75 | "country": "USA"
|
76 | },
|
77 | "votes": "lots"
|
78 | };
|
79 |
|
80 | v.addSchema(addressSchema, '/SimpleAddress');
|
81 | console.log(v.validate(p, schema));
|
82 | ```
|
83 | ### Example for Array schema
|
84 |
|
85 | ```json
|
86 | var arraySchema = {
|
87 | "type": "array",
|
88 | "items": {
|
89 | "properties": {
|
90 | "name": { "type": "string" },
|
91 | "lastname": { "type": "string" }
|
92 | },
|
93 | "required": ["name", "lastname"]
|
94 | }
|
95 | }
|
96 | ```
|
97 | For a comprehensive, annotated example illustrating all possible validation options, see [examples/all.js](./examples/all.js)
|
98 |
|
99 | ## Features
|
100 |
|
101 | ### Definitions
|
102 |
|
103 | All schema definitions are supported, $schema is ignored.
|
104 |
|
105 | ### Types
|
106 |
|
107 | All types are supported
|
108 |
|
109 | ### Handling `undefined`
|
110 |
|
111 | `undefined` is not a value known to JSON, and by default, the validator treats it as if it is not invalid. i.e., it will return valid.
|
112 |
|
113 | ```javascript
|
114 | var res = validate(undefined, {type: 'string'});
|
115 | res.valid // true
|
116 | ```
|
117 |
|
118 | This behavior may be changed with the "required" option:
|
119 |
|
120 | ```javascript
|
121 | var res = validate(undefined, {type: 'string'}, {required: true});
|
122 | res.valid // false
|
123 | ```
|
124 |
|
125 | ### Formats
|
126 |
|
127 | #### Disabling the format keyword.
|
128 |
|
129 | You may disable format validation by providing `disableFormat: true` to the validator
|
130 | options.
|
131 |
|
132 | #### String Formats
|
133 |
|
134 | All formats are supported, phone numbers are expected to follow the [E.123](http://en.wikipedia.org/wiki/E.123) standard.
|
135 |
|
136 | #### Custom Formats
|
137 |
|
138 | You may add your own custom format functions. Format functions accept the input
|
139 | being validated and return a boolean value. If the returned value is `true`, then
|
140 | validation succeeds. If the returned value is `false`, then validation fails.
|
141 |
|
142 | * Formats added to `Validator.prototype.customFormats` do not affect previously instantiated
|
143 | Validators. This is to prevent validator instances from being altered once created.
|
144 | It is conceivable that multiple validators may be created to handle multiple schemas
|
145 | with different formats in a program.
|
146 | * Formats added to `validator.customFormats` affect only that Validator instance.
|
147 |
|
148 | Here is an example that uses custom formats:
|
149 |
|
150 | ```javascript
|
151 | Validator.prototype.customFormats.myFormat = function(input) {
|
152 | return input === 'myFormat';
|
153 | };
|
154 |
|
155 | var validator = new Validator();
|
156 | validator.validate('myFormat', {type: 'string', format: 'myFormat'}).valid; // true
|
157 | validator.validate('foo', {type: 'string', format: 'myFormat'}).valid; // false
|
158 | ```
|
159 |
|
160 | ### Results
|
161 |
|
162 | By default, results will be returned in a `ValidatorResult` object with the following properties:
|
163 |
|
164 | * `instance`: any.
|
165 | * `schema`: Schema.
|
166 | * `errors`: ValidationError[].
|
167 | * `valid`: boolean.
|
168 |
|
169 | Each item in `errors` is a `ValidationError` with the following properties:
|
170 |
|
171 | * path: array. An array of property keys or array offsets, indicating where inside objects or arrays the instance was found.
|
172 | * property: string. Describes the property path. Starts with `instance`, and is delimited with a dot (`.`).
|
173 | * message: string. A human-readable message for debugging use. Provided in English and subject to change.
|
174 | * schema: object. The schema containing the keyword that failed
|
175 | * instance: any. The instance that failed
|
176 | * name: string. The keyword within the schema that failed.
|
177 | * argument: any. Provides information about the keyword that failed.
|
178 |
|
179 | The validator can be configured to throw in the event of a validation error:
|
180 |
|
181 | * If the `throwFirst` option is set, the validator will terminate validation at the first encountered error and throw a `ValidatorResultError` object.
|
182 |
|
183 | * If the `throwAll` option is set, the validator will throw a `ValidatorResultError` object after the entire instance has been validated.
|
184 |
|
185 | * If the `throwError` option is set, it will throw at the first encountered validation error (like `throwFirst`), but the `ValidationError` object itself will be thrown. Note that, despite the name, this does not inherit from Error like `ValidatorResultError` does.
|
186 |
|
187 | The `ValidatorResultError` object has the same properties as `ValidatorResult` and additionally inherits from Error.
|
188 |
|
189 | #### "nestedErrors" option
|
190 |
|
191 | When `oneOf` or `anyOf` validations fail, errors that caused any of the sub-schemas referenced therein to fail are normally suppressed, because it is not necessary to fix all of them. And in the case of `oneOf`, it would itself be an error to fix all of the listed errors.
|
192 |
|
193 | This behavior may be configured with `options.nestedErrors`. If truthy, it will emit all the errors from the subschemas. This option may be useful when troubleshooting validation errors in complex schemas:
|
194 |
|
195 | ```javascript
|
196 | var schema = {
|
197 | oneOf: [
|
198 | { type: 'string', minLength: 32, maxLength: 32 },
|
199 | { type: 'string', maxLength: 16 },
|
200 | { type: 'number' },
|
201 | ]
|
202 | };
|
203 | var validator = new Validator();
|
204 | var result = validator.validate('This string is 28 chars long', schema, {nestedErrors: true});
|
205 |
|
206 | // result.toString() reads out:
|
207 | // 0: instance does not meet minimum length of 32
|
208 | // 1: instance does not meet maximum length of 16
|
209 | // 2: instance is not of a type(s) number
|
210 | // 3: instance is not exactly one from [subschema 0],[subschema 1],[subschema 2]
|
211 | ```
|
212 |
|
213 | #### Localizing Error Messages
|
214 |
|
215 | To provide localized, human-readable errors, use the `name` string as a translation key. Feel free to open an issue for support relating to localizing error messages. For example:
|
216 |
|
217 | ```
|
218 | var localized = result.errors.map(function(err){
|
219 | return localeService.translate(err.name);
|
220 | });
|
221 | ```
|
222 |
|
223 | ### Custom keywords
|
224 |
|
225 | Specify your own JSON Schema keywords with the validator.attributes property:
|
226 |
|
227 | ```javascript
|
228 | validator.attributes.contains = function validateContains(instance, schema, options, ctx) {
|
229 | if(typeof instance !== 'string') return;
|
230 | if(typeof schema.contains !== 'string') throw new jsonschema.SchemaError('"contains" expects a string', schema);
|
231 | if(instance.indexOf(schema.contains)<0){
|
232 | return 'does not contain the string ' + JSON.stringify(schema.contains);
|
233 | }
|
234 | }
|
235 | var result = validator.validate("I am an instance", { type:"string", contains: "I am" });
|
236 | // result.valid === true;
|
237 | ```
|
238 |
|
239 | The instance passes validation if the function returns nothing. A single validation error is produced
|
240 | if the function returns a string. Any number of errors (maybe none at all) may be returned by passing a
|
241 | `ValidatorResult` object, which may be used like so:
|
242 |
|
243 | ```javascript
|
244 | var result = new ValidatorResult(instance, schema, options, ctx);
|
245 | while(someErrorCondition()){
|
246 | result.addError('fails some validation test');
|
247 | }
|
248 | return result;
|
249 | ```
|
250 |
|
251 | ### Dereferencing schemas
|
252 |
|
253 | Sometimes you may want to download schemas from remote sources, like a database, or over HTTP. When importing a schema,
|
254 | unknown references are inserted into the `validator.unresolvedRefs` Array. Asynchronously shift elements off this array and import
|
255 | them:
|
256 |
|
257 | ```javascript
|
258 | var Validator = require('jsonschema').Validator;
|
259 | var v = new Validator();
|
260 | v.addSchema(initialSchema);
|
261 | function importNextSchema(){
|
262 | var nextSchema = v.unresolvedRefs.shift();
|
263 | if(!nextSchema){ done(); return; }
|
264 | databaseGet(nextSchema, function(schema){
|
265 | v.addSchema(schema);
|
266 | importNextSchema();
|
267 | });
|
268 | }
|
269 | importNextSchema();
|
270 | ```
|
271 |
|
272 | ### Default base URI
|
273 |
|
274 | Schemas should typically have an `id` with an absolute, full URI. However if the schema you are using contains only relative URI references, the `base` option will be used to resolve these.
|
275 |
|
276 | This following example would throw a `SchemaError` if the `base` option were unset:
|
277 |
|
278 | ```javascript
|
279 | var result = validate(["Name"], {
|
280 | id: "/schema.json",
|
281 | type: "array",
|
282 | items: { $ref: "http://example.com/schema.json#/definitions/item" },
|
283 | definitions: {
|
284 | item: { type: "string" },
|
285 | },
|
286 | }, { base: 'http://example.com/' });
|
287 | ```
|
288 |
|
289 | ### Rewrite Hook
|
290 |
|
291 | The `rewrite` option lets you change the value of an instance after it has successfully been validated. This will mutate the `instance` passed to the validate function. This can be useful for unmarshalling data and parsing it into native instances, such as changing a string to a `Date` instance.
|
292 |
|
293 | The `rewrite` option accepts a function with the following arguments:
|
294 |
|
295 | * instance: any
|
296 | * schema: object
|
297 | * options: object
|
298 | * ctx: object
|
299 | * return value: any new value for the instance
|
300 |
|
301 | The value may be removed by returning `undefined`.
|
302 | If you don't want to change the value, call `return instance`.
|
303 |
|
304 | Here is an example that can convert a property expecting a date into a Date instance:
|
305 |
|
306 | ```javascript
|
307 | const schema = {
|
308 | properties: {
|
309 | date: {id: 'http://example.com/date', type: 'string'},
|
310 | },
|
311 | };
|
312 |
|
313 | const value = {
|
314 | date: '2020-09-30T23:39:27.060Z',
|
315 | };
|
316 |
|
317 | function unmarshall(instance, schema){
|
318 | if(schema.id === 'http://example.com/date'){
|
319 | return new Date(instance);
|
320 | }
|
321 | return instance;
|
322 | }
|
323 |
|
324 | const v = new Validator();
|
325 | const res = v.validate(value, schema, {rewrite: unmarshall});
|
326 |
|
327 | assert(res.instance.date instanceof Date);
|
328 | ```
|
329 |
|
330 |
|
331 | ### Pre-Property Validation Hook
|
332 |
|
333 | If some processing of properties is required prior to validation a function may be passed via the options parameter of the validate function. For example, say you needed to perform type coercion for some properties:
|
334 |
|
335 | ```javascript
|
336 | // See examples/coercion.js
|
337 | function preValidateProperty(object, key, schema, options, ctx) {
|
338 | var value = object[key];
|
339 | if (typeof value === 'undefined') return;
|
340 |
|
341 | // Test if the schema declares a type, but the type keyword fails validation
|
342 | if (schema.type && validator.attributes.type.call(validator, value, schema, options, ctx.makeChild(schema, key))) {
|
343 | // If the type is "number" but the instance is not a number, cast it
|
344 | if(schema.type==='number' && typeof value!=='number'){
|
345 | object[key] = parseFloat(value);
|
346 | return;
|
347 | }
|
348 | // If the type is "string" but the instance is not a string, cast it
|
349 | if(schema.type==='string' && typeof value!=='string'){
|
350 | object[key] = String(value).toString();
|
351 | return;
|
352 | }
|
353 | }
|
354 | };
|
355 |
|
356 | // And now, to actually perform validation with the coercion hook!
|
357 | v.validate(instance, schema, { preValidateProperty });
|
358 | ```
|
359 |
|
360 | ### Skip validation of certain keywords
|
361 |
|
362 | Use the "skipAttributes" option to skip validation of certain keywords. Provide an array of keywords to ignore.
|
363 |
|
364 | For skipping the "format" keyword, see the disableFormat option.
|
365 |
|
366 | ### Fail on unknown keywords
|
367 |
|
368 | By default, JSON Schema is supposed to ignore unknown schema keywords.
|
369 |
|
370 | You can change this behavior to require that all keywords used in a schema have a defined behavior, by using setting the "allowUnknownAttributes" option to false.
|
371 |
|
372 | This example will throw a `SchemaError`:
|
373 |
|
374 | ```javascript
|
375 | var schema = {
|
376 | type: "string",
|
377 | format: "email",
|
378 | example: "foo",
|
379 | };
|
380 | var result = validate("Name", schema, { allowUnknownAttributes: false });
|
381 | ```
|
382 |
|
383 | ## Tests
|
384 |
|
385 | Uses [JSON Schema Test Suite](https://github.com/json-schema/JSON-Schema-Test-Suite) as well as our own tests.
|
386 | You'll need to update and init the git submodules:
|
387 |
|
388 | git submodule update --init
|
389 | npm test
|
390 |
|
391 | ## Contributions
|
392 |
|
393 | This library would not be possible without the valuable contributions by:
|
394 |
|
395 | - Austin Wright
|
396 |
|
397 | ... and many others!
|
398 |
|
399 | ## License
|
400 |
|
401 | jsonschema is licensed under MIT license.
|
402 |
|
403 | Copyright (C) 2012-2019 Tom de Grunt <tom@degrunt.nl>
|
404 |
|
405 | Permission is hereby granted, free of charge, to any person obtaining a copy of
|
406 | this software and associated documentation files (the "Software"), to deal in
|
407 | the Software without restriction, including without limitation the rights to
|
408 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
409 | of the Software, and to permit persons to whom the Software is furnished to do
|
410 | so, subject to the following conditions:
|
411 |
|
412 | The above copyright notice and this permission notice shall be included in all
|
413 | copies or substantial portions of the Software.
|
414 |
|
415 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
416 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
417 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
418 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
419 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
420 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
421 | SOFTWARE.
|