GithubHelp home page GithubHelp logo

z-schema's Introduction

z-schema validator

npm version bower version build status coverage status

Greenkeeper badge

NPM

  • version 3.0 runs also in the browsers now, run tests yourself here

Topics

Usage

Validator will try to perform sync validation when possible for speed, but supports async callbacks when they are necessary.

Development:

These repository has several submodules and should be cloned as follows:

git clone --recursive https://github.com/zaggino/z-schema.git

CLI:

npm install --global z-schema
z-schema --help
z-schema mySchema.json
z-schema mySchema.json myJson.json
z-schema --strictMode mySchema.json myJson.json

NodeJS:

var ZSchema = require("z-schema");
var options = ... // see below for possible option values
var validator = new ZSchema(options);

Sync mode:

var valid = validator.validate(json, schema);
// this will return a native error object with name and message
var error = validator.getLastError();
// this will return an array of validation errors encountered
var errors = validator.getLastErrors();
...

Async mode:

validator.validate(json, schema, function (err, valid) {
    ...
});

Browser:

<script type="text/javascript" src="../dist/ZSchema-browser-min.js"></script>
<script type="text/javascript">
	var validator = new ZSchema();
	var valid = validator.validate("string", { "type": "string" });
	console.log(valid);
</script>

Remote references and schemas:

In case you have some remote references in your schemas, you have to download those schemas before using validator. Otherwise you'll get UNRESOLVABLE_REFERENCE error when trying to compile a schema.

var validator = new ZSchema();
var json = {};
var schema = { "$ref": "http://json-schema.org/draft-04/schema#" };

var valid = validator.validate(json, schema);
var errors = validator.getLastErrors();
// valid === false
// errors.length === 1
// errors[0].code === "UNRESOLVABLE_REFERENCE"

var requiredUrl = "http://json-schema.org/draft-04/schema";
request(requiredUrl, function (error, response, body) {

    validator.setRemoteReference(requiredUrl, JSON.parse(body));

    var valid = validator.validate(json, schema);
    var errors = validator.getLastErrors();
    // valid === true
    // errors === undefined

}

If you're able to load schemas synchronously, you can use ZSchema.setSchemaReader feature:

ZSchema.setSchemaReader(function (uri) {
    var someFilename = path.resolve(__dirname, "..", "schemas", uri + ".json");
    return JSON.parse(fs.readFileSync(someFilename, "utf8"));
});

Features

Validate against subschema

In case you don't want to split your schema into multiple schemas using reference for any reason, you can use option schemaPath when validating:

var valid = validator.validate(cars, schema, { schemaPath: "definitions.car.definitions.cars" });

See more details in the test.

Compile arrays of schemas and use references between them

You can use validator to compile an array of schemas that have references between them and then validate against one of those schemas:

var schemas = [
    {
        id: "personDetails",
        type: "object",
        properties: {
            firstName: { type: "string" },
            lastName: { type: "string" }
        },
        required: ["firstName", "lastName"]
    },
    {
        id: "addressDetails",
        type: "object",
        properties: {
            street: { type: "string" },
            city: { type: "string" }
        },
        required: ["street", "city"]
    },
    {
        id: "personWithAddress",
        allOf: [
            { $ref: "personDetails" },
            { $ref: "addressDetails" }
        ]
    }
];

var data = {
    firstName: "Martin",
    lastName: "Zagora",
    street: "George St",
    city: "Sydney"
};

var validator = new ZSchema();

// compile & validate schemas first, z-schema will automatically handle array
var allSchemasValid = validator.validateSchema(schemas);
// allSchemasValid === true

// now validate our data against the last schema
var valid = validator.validate(data, schemas[2]);
// valid === true

Register a custom format

You can register any format of your own. Your sync validator function should always respond with a boolean:

ZSchema.registerFormat("xstring", function (str) {
    return str === "xxx";
});

Async format validators are also supported, they should accept two arguments, value and a callback to which they need to respond:

ZSchema.registerFormat("xstring", function (str, callback) {
    setTimeout(function () {
        callback(str === "xxx");
    }, 1);
});

Helper method to check the formats that have been registered

var registeredFormats = ZSchema.getRegisteredFormats();
//registeredFormats will now contain an array of all formats that have been registered with z-schema

Automatic downloading of remote schemas

Automatic downloading of remote schemas was removed from version 3.x but is still possible with a bit of extra code, see this test for more information on this.

Prefill default values to object using format

Using format, you can pre-fill values of your choosing into the objects like this:

ZSchema.registerFormat("fillHello", function (obj) {
    obj.hello = "world";
    return true;
});

var data = {};

var schema = {
    "type": "object",
    "format": "fillHello"
};

validator.validate(data, schema);
// data.hello === "world"

Options

asyncTimeout

Defines a time limit, which should be used when waiting for async tasks like async format validators to perform their validation, before the validation fails with an ASYNC_TIMEOUT error.

var validator = new ZSchema({
    asyncTimeout: 2000
});

noEmptyArrays

When true, validator will assume that minimum count of items in any array is 1, except when minItems: 0 is explicitly defined.

var validator = new ZSchema({
    noEmptyArrays: true
});

noEmptyStrings

When true, validator will assume that minimum length of any string to pass type string validation is 1, except when minLength: 0 is explicitly defined.

var validator = new ZSchema({
    noEmptyStrings: true
});

noTypeless

When true, validator will fail validation for schemas that don't specify a type of object that they expect.

var validator = new ZSchema({
    noTypeless: true
});

noExtraKeywords

When true, validator will fail for schemas that use keywords not defined in JSON Schema specification and doesn't provide a parent schema in $schema property to validate the schema.

var validator = new ZSchema({
    noExtraKeywords: true
});

assumeAdditional

When true, validator assumes that additionalItems/additionalProperties are defined as false so you don't have to manually fix all your schemas.

var validator = new ZSchema({
    assumeAdditional: true
});

When an array, validator assumes that additionalItems/additionalProperties are defined as false, but allows some properties to pass.

var validator = new ZSchema({
    assumeAdditional: ["$ref"]
});

forceAdditional

When true, validator doesn't validate schemas where additionalItems/additionalProperties should be defined to either true or false.

var validator = new ZSchema({
    forceAdditional: true
});

forceItems

When true, validator doesn't validate schemas where items are not defined for array type schemas. This is to avoid passing anything through an array definition.

var validator = new ZSchema({
    forceItems: true
});

forceMinItems

When true, validator doesn't validate schemas where minItems is not defined for array type schemas. This is to avoid passing zero-length arrays which application doesn't expect to handle.

var validator = new ZSchema({
    forceMinItems: true
});

forceMaxItems

When true, validator doesn't validate schemas where maxItems is not defined for array type schemas. This is to avoid passing arrays with unlimited count of elements which application doesn't expect to handle.

var validator = new ZSchema({
    forceMaxItems: true
});

forceMinLength

When true, validator doesn't validate schemas where minLength is not defined for string type schemas. This is to avoid passing zero-length strings which application doesn't expect to handle.

var validator = new ZSchema({
    forceMinLength: true
});

forceMaxLength

When true, validator doesn't validate schemas where maxLength is not defined for string type schemas. This is to avoid passing extremly large strings which application doesn't expect to handle.

var validator = new ZSchema({
    forceMaxLength: true
});

forceProperties

When true, validator doesn't validate schemas where properties or patternProperties is not defined for object type schemas. This is to avoid having objects with unexpected properties in application.

var validator = new ZSchema({
    forceProperties: true
});

ignoreUnresolvableReferences

When true, validator doesn't end with error when a remote reference is unreachable. This setting is not recommended in production outside of testing.

var validator = new ZSchema({
    ignoreUnresolvableReferences: true
});

enumCaseInsensitiveComparison

When true, validator will return a ENUM_CASE_MISMATCH when the enum values mismatch only in case.

var validator = new ZSchema({
    enumCaseInsensitiveComparison: true
});

strictUris

When true, all strings of format uri must be an absolute URIs and not only URI references. See more details in this issue.

var validator = new ZSchema({
    strictUris: true
});

strictMode

Strict mode of z-schema is currently equal to the following:

if (this.options.strictMode === true) {
    this.options.forceAdditional  = true;
    this.options.forceItems       = true;
    this.options.forceMaxLength   = true;
    this.options.forceProperties  = true;
    this.options.noExtraKeywords  = true;
    this.options.noTypeless       = true;
    this.options.noEmptyStrings   = true;
    this.options.noEmptyArrays    = true;
}
var validator = new ZSchema({
    strictMode: true
});

breakOnFirstError

default: false
When true, will stop validation after the first error is found:

var validator = new ZSchema({
    breakOnFirstError: true
});

reportPathAsArray

Report error paths as an array of path segments instead of a string:

var validator = new ZSchema({
    reportPathAsArray: true
});

ignoreUnknownFormats

By default, z-schema reports all unknown formats, formats not defined by JSON Schema and not registered using ZSchema.registerFormat, as an error. But the JSON Schema specification says that validator implementations "they SHOULD offer an option to disable validation" for format. That being said, setting this option to true will disable treating unknown formats as errlrs

var validator = new ZSchema({
    ignoreUnknownFormats: true
});

includeErrors

By default, z-schema reports all errors. If interested only in a subset of the errors, passing the option includeErrors to validate will perform validations only for those errors.

var validator = new ZSchema();
// will only execute validation for "INVALID_TYPE" error.
validator.validate(json, schema, {includeErrors: ["INVALID_TYPE"]});

customValidator

Warning: Use only if know what you are doing. Always consider using custom format before using this option.

Register function to be called as part of validation process on every subshema encounter during validation.

Let's make a real-life example with this feature. Imagine you have number of transactions:

{
    "fromId": 1034834329,
    "toId": 1034834543,
    "amount": 200
}

So you write the schema:

{
    "type": "object",
    "properties": {
        "fromId": {
            "type": "integer"
        },
        "toId": {
            "type": "integer"
        },
        "amount": {
            "type": "number"
        }
    }
}

But how to check that fromId and toId are never equal. In JSON Schema Draft4 there is no possibility to do this. Actually, it's easy to just write validation code for such simple payloads. But what if you have to do the same check for many objects in different places of JSON payload. One solution is to add custom keyword uniqueProperties with array of property names as a value. So in our schema we would need to add:

"uniqueProperties": [
    "fromId",
    "toId"
]

To teach z-schema about this new keyword we need to write handler for it:

function customValidatorFn(report, schema, json) {
    // check if our custom property is present
    if (Array.isArray(schema.uniqueProperties)) {
        var seenValues = [];
        schema.uniqueProperties.forEach(function (prop) {
            var value = json[prop];
            if (typeof value !== 'undefined') {
                if (seenValues.indexOf(value) !== -1) {
                    // report error back to z-schema core
                    report.addCustomError("NON_UNIQUE_PROPERTY_VALUE",
                        "Property \"{0}\" has non-unique value: {1}",
                        [prop, value], null, schema.description);
                }
                seenValues.push(value)
            }
        });
    }
}

var validator = new ZSchema({
    // register our custom validator inside z-schema
    customValidator: customValidatorFn
});

Let's test it:

var data = {
    fromId: 1034834346,
    toId: 1034834346,
    amount: 50
};

validator.validate(data, schema);
console.log(validator.getLastErrors())
//[ { code: 'NON_UNIQUE_PROPERTY_VALUE',
//    params: [ 'toId', 1034834346 ],
//    message: 'Property "toId" has non-unique value: 1034834346',
//    path: '#/',
//    schemaId: undefined } ]

Note: before creating your own keywords you should consider all compatibility issues.

Benchmarks

So how does it compare to version 2.x and others?

NOTE: these tests are purely orientational, they don't consider extra features any of the validator may support and implement

rawgithub.com/zaggino/z-schema/master/benchmark/results.html

Contributors

Thanks for contributing to:

and to everyone submitting issues on GitHub

z-schema's People

Contributors

addaleax avatar antialias avatar barrtender avatar dependabot[bot] avatar dgerber avatar domoritz avatar epoberezkin avatar figadore avatar geraintluff avatar greenkeeper[bot] avatar hanoterlin avatar hirse avatar ivangoncharov avatar jfromaniello avatar kallaspriit avatar keiii avatar mattisg avatar mctep avatar nvenegas avatar pgonzal avatar romanhotsiy avatar sandersky avatar santam85 avatar sauvainr avatar semigradsky avatar sergey-shandar avatar simon-p-r avatar thetoolbox avatar whitlockjc avatar zaggino avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

z-schema's Issues

The "validateSchema" method with callback function doesn't actually validate.

It looks like the "validateSchema" method doesn't actually validate the given schema:

var ZSchema = require('z-schema');
var validator = new ZSchema();
validator.validateSchema('bogus schema here', function(err, report) {
  if (err) throw err;
  console.log(report); // { valid: true, errors: [], warnings: [] }
});

missing files in 1.4.0

not sure if I am going crazy, but when I try to NPM install this library, no code is actually included in the package. There's no .js files inside node_modules/z-schema and requiring the library doesn't work.

afischer-mba ~ $ npm install z-schema
npm http GET https://registry.npmjs.org/z-schema
npm http 304 https://registry.npmjs.org/z-schema
npm http GET https://registry.npmjs.org/q
npm http 304 https://registry.npmjs.org/q
[email protected] node_modules/z-schema
โ””โ”€โ”€ [email protected]
afischer-mba ~ $ ls node_modules/z-schema/
LICENSE     README.md   node_modules    package.json
afischer-mba ~ $ node
> require('z-schema')
Error: Cannot find module 'z-schema'
    at Function.Module._resolveFilename (module.js:338:15)
    at Function.Module._load (module.js:280:25)
    at Module.require (module.js:362:17)
    at require (module.js:378:17)
    at repl:1:2
    at REPLServer.self.eval (repl.js:111:21)
    at Interface.<anonymous> (repl.js:250:12)
    at Interface.EventEmitter.emit (events.js:93:17)
    at Interface._onLine (readline.js:199:10)
    at Interface._line (readline.js:517:8)

Error paths as Array

What do you think about enabling, via some configuration, the representation of error paths? On the one hand, I like the JSON Pointer syntax for presentation but on the other, I find an array of path segments to be much easier to work with from a programmatic perspective.

sync validate method doesn't work

I just tried to follow your example, using [var validator = new ZSchema({ sync: true }); ] and I have this whole Failure Trace...

TypeError: Object true has no method 'forEach'
at ZSchema.InstanceValidators.required (\node_modules\z-schema\src\ZSchema.js:1905:29)
at step1 (\node_modules\z-schema\src\ZSchema.js:1223:48)
at Object.Utils.forEach (\node_modules\z-schema\src\ZSchema.js:195:34)
at ZSchema._validateObject (\node_modules\z-schema\src\ZSchema.js:1244:19)
at ZSchema.<anonymous> (\node_modules\z-schema\src\ZSchema.js:1383:26)
at Array.forEach (native)
at ZSchema.<anonymous> (\node_modules\z-schema\src\ZSchema.js:1380:15)
at Object.Utils.forEach (\node_modules\z-schema\src\ZSchema.js:195:34)
at ZSchema._recurseObject (\node_modules\z-schema\src\ZSchema.js:1354:15)
at step2 (\node_modules\z-schema\src\ZSchema.js:1232:29)
at ZSchema._validateObject (\node_modules\z-schema\src\ZSchema.js:1245:13)
at ZSchema.validate (\node_modules\z-schema\src\ZSchema.js:792:18)

Handle a Date instance?

I work mostly in Node and front-end javascript. I like Z-Schema, but it forces me to convert the date to a String, then back to a Date after validation. It's a little cumbersome because mongodb allows storing Date instances into the db. How could I extend or modify z-schema to handle a Date instance? Or could we add the functionality into Z-Schema?

noTypeless behavior when using anyof

I think this case should be valid, but currently throws an error. The current implementation only looks for type on the anyOf schemas and not the parent.

type: 'object',
properties: {
    'prop1': {...}
    'prop2': {...},
},
anyOf: [
    {
        required: ['prop1']
    },
    {
        required: ['prop2']
    }
]

missing references?

hi @zaggino ,
i have a question/issue.. having schema (taken from your example) formatted as object :

{
  "id": "root.json",
  "person": {
    "id": "personDetails",
    "type": "object",
    "properties": {
      "firstName": {
        "type": "string"
      },
      "lastName": {
        "type": "string"
      }
    },
    "required": ["firstName", "lastName"]
  },
  "addr": {
    "id": "addressDetails",
    "type": "object",
    "properties": {
      "street": {
        "type": "string"
      },
      "city": {
        "type": "string"
      }
    },
    "required": ["street", "city"]
  },
  "peraddr": {
    "id": "personWithAddress",
    "allOf": [{
      "$ref": "#personDetails"
    }, {
      "$ref": "#addressDetails"
    }, {
      "$ref": "#/yy"
    }, {
      "$ref": "#xx"
    }]
  }

and code:

  allSchemasValid = validator.validateSchema(rootschema);
  // allSchemasValid === flase

  comp_res = validator.compileSchema(rootschema);
  // comp_res === flase

  err = validator.getLastErrors()
/*
err == [
 {
  code: "UNRESOLVABLE_REFERENCE"
  message: "Reference could not be resolved: #/yy"
  params: [  "#/yy" ]
  path: "#/peraddr/allOf/2"
 }
]
*/

miss =validator.getMissingReferences()
/*
miss == [
""
]
*/

is validator.getMissingReferences() return as it should be?

Recompiling schemas results in 'Reference could not be resolved'

One of the projects I'm working on has a handful of schema files that get combined together to validate a complete JSON payload but there is also a need/desire to be able to treat these schema files individually and validate a JSON payload fragment. Below is an example structure:

var schemaCombinations = {
  'authorizationObject.json': [
    oauth2GrantType, // 'oauth2GrantType.json
    authorizationObject // 'authorizationObject.json
  ],
  'infoObject.json': [
    infoObject // infoObject.json
  ],
  'oauth2GrantType.json': [
    oauth2GrantType // oauth2GrantType.json
  ],
  'resourceObject.json': [
    resourceObject // resourceObject.json
  ],
  'resourceListing.json': [
    resourceObject, // resourceObject.json
    infoObject, // infoObject.json
    oauth2GrantType, // oauth2GrantType.json
    authorizationObject, // authorizationObject.json
    resourceListing // resourceListing.json
  ]
};

So each of these schema combinations represent the array of schema fragments to compile for the corresponding schema file to be valid. So each array is basically a list of dependencies, including itself.

If I had code to create a validator for each schema file, the first 4 compile just fine but the last one fails due to the following references not resolving: resourceObject.json, infoObject.json and authorizationObject.json. I'm not sure why that would happen. On a whim, I updated the same code to avoid building the first four validators and only build the last one and it works. Below is the code to replicate this. To reproduce the current problem, which happens when all 5 validators are created in the same program, run it as-is. To see the scenario where the failing validator compiles properly when the first four validators are not created prior, comment out Block One and uncomment Block Two.

'use strict';

var ZSchema = require('z-schema');
var resourceObject = {
  'id': 'resourceObject.json',
  '$schema': 'http://json-schema.org/draft-04/schema#',
  'type': 'object',
  'required': [ 'path' ],
  'properties': {
        'path': { 'type': 'string', 'format': 'uri' },
    'description': { 'type': 'string' }
  },
  'additionalProperties': false
};
var infoObject = {
  'id': 'infoObject.json',
  '$schema': 'http://json-schema.org/draft-04/schema#',
  'description': 'info object (section 5.1.3)',
  'type': 'object',
  'required': [ 'title', 'description' ],
  'properties': {
    'title': { 'type': 'string' },
    'description': { 'type': 'string' },
    'termsOfServiceUrl': { 'type': 'string', 'format': 'uri' },
    'contact': { 'type': 'string', 'format': 'email' },
    'license': { 'type': 'string' },
    'licenseUrl': { 'type': 'string', 'format': 'uri' }
  },
  'additionalProperties': false
};
var oauth2GrantType = {
  'id': 'oauth2GrantType.json',
  '$schema': 'http://json-schema.org/draft-04/schema#',
  'type': 'object',
  'minProperties': 1,
  'properties': {
    'implicit': { '$ref': '#/definitions/implicit' },
    'authorization_code': { '$ref': '#/definitions/authorizationCode' }
  },
  'definitions': {
    'implicit': {
      'type': 'object',
      'required': [ 'loginEndpoint' ],
      'properties': {
        'loginEndpoint': { '$ref': '#/definitions/loginEndpoint' },
        'tokenName': { 'type': 'string' }
      },
      'additionalProperties': false
    },
    'authorizationCode': {
      'type': 'object',
      'required': [ 'tokenEndpoint', 'tokenRequestEndpoint' ],
      'properties': {
        'tokenEndpoint': { '$ref': '#/definitions/tokenEndpoint' },
        'tokenRequestEndpoint': { '$ref': '#/definitions/tokenRequestEndpoint' }
      },
      'additionalProperties': false
    },
    'loginEndpoint': {
      'type': 'object',
      'required': [ 'url' ],
      'properties': {
        'url': { 'type': 'string', 'format': 'uri' }
      },
      'additionalProperties': false
    },
    'tokenEndpoint': {
      'type': 'object',
      'required': [ 'url' ],
      'properties': {
        'url': { 'type': 'string', 'format': 'uri' },
        'tokenName': { 'type': 'string' }
      },
      'additionalProperties': false
    },
    'tokenRequestEndpoint': {
      'type': 'object',
      'required': [ 'url' ],
      'properties': {
        'url': { 'type': 'string', 'format': 'uri' },
        'clientIdName': { 'type': 'string' },
        'clientSecretName': { 'type': 'string' }
      },
      'additionalProperties': false
    }
  }
};
var authorizationObject = {
  'id': 'authorizationObject.json',
  '$schema': 'http://json-schema.org/draft-04/schema#',
  'type': 'object',
  'additionalProperties': {
    'oneOf': [
      {
        '$ref': '#/definitions/basicAuth'
      },
      {
        '$ref': '#/definitions/apiKey'
      },
      {
        '$ref': '#/definitions/oauth2'
      }
    ]
  },
  'definitions': {
    'basicAuth': {
      'required': [ 'type' ],
      'properties': {
        'type': { 'enum': [ 'basicAuth' ] }
      },
      'additionalProperties': false
    },
    'apiKey': {
      'required': [ 'type', 'passAs', 'keyname' ],
      'properties': {
        'type': { 'enum': [ 'apiKey' ] },
        'passAs': { 'enum': [ 'header', 'query' ] },
        'keyname': { 'type': 'string' }
      },
      'additionalProperties': false
    },
    'oauth2': {
      'type': 'object',
      'required': [ 'type', 'grantTypes' ],
      'properties': {
        'type': { 'enum': [ 'oauth2' ] },
        'scopes': {
          'type': 'array',
          'items': { '$ref': '#/definitions/oauth2Scope' }
        },
        'grantTypes': { '$ref': 'oauth2GrantType.json' }
      },
      'additionalProperties': false
    },
    'oauth2Scope': {
      'type': 'object',
      'required': [ 'scope' ],
      'properties': {
        'scope': { 'type': 'string' },
        'description': { 'type': 'string' }
      },
      'additionalProperties': false
    }
  }
};
var resourceListing = {
  'id': 'resourceListing.json',
  '$schema': 'http://json-schema.org/draft-04/schema#',
  'type': 'object',
  'required': [ 'swaggerVersion', 'apis' ],
  'properties': {
    'swaggerVersion': { 'enum': [ '1.2' ] },
    'apis': {
      'type': 'array',
      'items': { '$ref': 'resourceObject.json' }
    },
    'apiVersion': { 'type': 'string' },
    'info': { '$ref': 'infoObject.json' },
    'authorizations': { '$ref': 'authorizationObject.json' }
  }
};

var schemaCombinations = {
  'authorizationObject.json': [
    oauth2GrantType,
    authorizationObject
  ],
  'infoObject.json': [
    infoObject
  ],
  'oauth2GrantType.json': [
    oauth2GrantType
  ],
  'resourceObject.json': [
    resourceObject
  ],
  'resourceListing.json': [
    resourceObject,
    infoObject,
    oauth2GrantType,
    authorizationObject,
    resourceListing
  ]
};

// Block One
Object.keys(schemaCombinations).forEach(function (name) {
  var validator = new ZSchema({sync: true});
  var toCompile = schemaCombinations[name];

  try {
    validator.compileSchema(toCompile);

    console.log(name +': success');
  } catch (err) {
    var vError = validator.getLastError();

    if (!vError.errors) {
      vError = err;
    }

    console.log(name +': failure');

    if (vError.errors) {
      vError.errors.forEach(function (error) {
        console.log('  ' + error.message);
      });
    } else {
      console.log('  ' + vError.message);
    }
  }
});

// Block Two
// new ZSchema({sync: true}).compileSchema(schemaCombinations['resourceListing.json']);

Support for meta data keywords and custom meta data keywords

It would be really great if it was easier to do the following things with Z-Schema

  1. Actually fill in default values into a json instance using the validator.
    This is something which is a real pain especially because of javascript lack of strict type checking. It would be great to be able to ignore keys and just have the validator fill in the default values instead of doing it in a separate piece of code.
  2. Add custom meta data keywords for additional data transformations
    I believe filling in default values would be considered as a meta data operation applied to the json instance before actually performing the validation. Making this operation customizable would be a real time saver for many common scenarios.

The case for a customizable meta data value would be scenario where you may have to perform additional processing on the data before or after validation besides just putting a default value.

For example if we wish to sanitize html which is submitted to the server as a json request. I could simply implement a meta data boolean keyword called sanitize which would then sanitize the input before validating it.

It could also be used for perhaps converting date-time strings to Date or serialized mongoDB objectIDs to ObjectID instances.

"links" warning in Hyper-Schema validation

Tries to validate simple hyper-schemas with empty links array, and got a warning telling me that links is unknown key despite I set $schema to hyper-schema url explicitly:

var ZSchema = require('z-schema');

var schema = {
    $schema: "http://json-schema.org/draft-04/hyper-schema#",
    links: []
};

var validator = new ZSchema();

validator.validateSchema(schema, function (err, report) {
    console.log(report);
});

gives

{ valid: true,
  errors: [],
  warnings: [ { message: 'Unknown key "links" found in schema.', path: '#/' } ] }

Am I right, that for now schemas are not validated against schema specified by URI in $schema key?

type coercion

In the same way you can register a validator, would it be possible to register a coercion function? The idea would be if you have a field you know is a timestamp, you could coerce it to a Date object, or if you had an attribute that was an array of Users which you have a class constructor for, you could have z-schema convert those to your local type.

nested hierarchy

Hi,

I have nested hierarchy like this:

{
"is_and": false,
"filters": [
{
"is_and": false,
"filters": [
{
"is_and": true,
"filters": [
{
"is_and": true,
"filters": [
{
"is_and": true,
"filters": [
{
"is_and": true,
"filters": [
{
"text": 'ABC',
"is_last": true,
"filters": [

                      ]
                    }
                  ]
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}

]
}

Now i want to validate the above json schema with self referencing but issue is that the last item in nested hierarchy has different properties & filter object being empty too, so having trouble for terminating the last item in hierarchy.

anyone suggest short validation rule.

Thanks

Clarification on the schema/reference id and the trailing hash

The Plugging this into our main schema example of the JSON Schema - Advanced Example shows an example of a $ref with a trailing # at the end. If I have a schema id of mySchema and I reference it from another document with a $ref of mySchema#, I'd expect that to work. But with z-schema, this does not work. For the reference to work, I either have to change my schema.id to mySchema# or change my $ref to be mySchema. Below is an example that will result in an unresolvable reference. Is the # suppose to be significant in the id/reference?

var ZSchema = require('z-schema');
var validator = new ZSchema({sync: true});
var schemaA = {
  id: 'schemaA',
  type: 'string'
};
var schemaB = {
  id: 'schemaB',
  properties: {
    a: {
      // '$ref': 'schemaA' // Works
      '$ref': 'schemaA#' // Fails
    }
  }
};

try {
  validator.compileSchema([schemaA, schemaB]);
} catch (err) {
  var vError = validator.getLastError();

  if (vError && vError.valid === false) {
    console.log(validator.getLastError());
  } else {
    throw err;
  }
}

validation ok despite missing reference

{a: 4}

is validated successfully against

{
    $schema: 'http://json-schema.org/draft-04/schema#',
    id: "schemaA",
    type: "object",
    properties: {
      a: {
        type: "integer"
      },
      b: {
        type: "string"
      },
      c: {
        $ref: 'schemaB'
      }
    },
    required: ['a']
  }

Nested hierarchy without explicit leaf

Hello,
I have a nested hierarchy and I don't want to explicitly define the last item as was done in #22. When I remove the oneOf construct it seems to end in an infinite loop.

This is the json/schema exhibiting the issue:
JSON

{ "is_start": true,
    "hierarchy": {
        "is_and": false,
        "filters": [
            {
                "is_and": false,
                "filters": [
                    {
                        "is_and": true,
                        "filters": [
                            {
                                "is_and": true,
                                "filters": [
                                    {
                                        "is_and": true,
                                        "filters": [
                                            {
                                                "is_and": true,
                                                "filters": [
                                                    {
                                                        "is_and": true,
                                                        "filters": []
                                                    }
                                                ]
                                            }
                                        ]
                                    }
                                ]
                            }
                        ]
                    }
                ]
            }
        ]
    }}

SCHEMA

{
    "$schema": "http://json-schema.org/draft-04/schema#",
    "title": "Product set",
    "is_start": "boolean",
    "hierarchy": {
        "$ref": "#/definitions/recursion"
    },
    "definitions": {
        "recursion": {
            "type": "object",
            "additionalProperties": false,
            "properties": {
                "is_and": {
                    "type": "boolean"
                },
                "filters": {
                    "type": "array",
                    "additionalItems": false,
                    "items": {
                        "$ref": "#/definitions/recursion"
                    }
                }
            }
        }
    }
}

registerFormat has no effect on validation result

I noticed today that my custom format is not having any effect on the end result. I always get successful validation. Is there more I need to do to register my custom format? Provide some kind of error message?

ZSchema.registerFormat('date-object', function (obj) {
    //return obj instanceof Date;
    return false;  // Still passes
});

ZSchema.registerFormat('object-id', function (obj) {
    //return obj instanceof ObjectID;
    return false;  // Still passes
});

Here is part of my schema:

'notes': {
    type: 'array',
    items: {
        type: 'object',
        properties: {
            'note': { type: 'string', maxLength: 2048 },
            'created': { type: 'object', format: 'date-object' },
            'createdBy': { type: 'string', maxLength: 161 },
            '_id': { type: 'object', format: 'object-id' }
        }
    }
}

Validation errors on non-required fields

Hi,

First of all, thanks for this great library!
I've encountered some problems validating a schema with optional/non-required properties.
I get this error:

errors: Array[2]
0: ValidationError
code: "FORMAT"
message: "date format validation failed: "
path: "#/date_of_birth"
1: ValidationError
code: "PATTERN"
message: "String does not match pattern: ^[1-9]{1}[0-9]{3} ?[A-Z]{2}$"
params: Object
path: "#/postcode"
valid: false

on this schema:
"website_users":
{
"$schema": "http://json-schema.org/schema#",
"description": "Schema for the website users context",
"type": "object",
"self": {
"vendor": "xxxx",
"name": "website_users",
"format": "jsonschema",
"version": "1-0-0"
},
"properties": {

        "date_of_birth": {
            "type": "string",
            "format": "date" 
        },

        "dealer_postcode": {
            "type": "string",
            "pattern": "^[1-9]{1}[0-9]{3} ?[A-Z]{2}$"
        },
        "email": {
            "type": "string",
            "maxLength": 128
        },
        "email_id": {
            "type": "string",
            "maxLength": 64
        },
        "firstname": {
            "type": "string",
            "maxLength": 64
        },
        "gender": {
            "type": "string", "enum":["man","vrouw"]
        },
        "housenumber": {
            "type": "string",
            "maxLength": 16
        },
        "initials": {
            "type": "string",
            "maxLength": 8
        },
        "kvk_nummer": {
            "type": "string",
            "maxLength": 32
        },
        "language": {
            "type": "string",
            "maxLength": 16
        },
        "lastname": {
            "type": "string",
            "maxLength": 64
        },

        "phonenumber": {
            "type": "string",
            "maxLength": 32
        },
        "postcode": {
            "type": "string",
            "pattern": "^[1-9]{1}[0-9]{3} ?[A-Z]{2}$"
        },
        "street": {
            "type": "string",
            "maxLength": 64
        },
        "tussenvoegsel": {
            "type": "string",
            "maxLength": 16
        },
        "user_id": {
            "type": "string",
            "maxLength": 32
        }
    },
    "required": ["email"],
    "additionalProperties": false
}
  • This is just a fragment of the complete json schema.

I understand that the validation fails on these fields (because they are empty) but they are optional.

Thanks in advance!

Reason for creating this library?

I'd like to know why development of this library was started although there are JSON schema validation libraries out there? What are the features of this library which make z-schema different from other libraries (i.e. natesilva/jayschema)? Is it the Promises/A+ based API?

benchmarks addition?

Hi @zaggino

I recently created yet another javascript json-schema library called jjv, hosted here:
https://github.com/acornejo/jjv

Due to security and performance reasons, it does not support downloading remote references. Aside from that it should be draft4 compatible (it passes all appropriate tests).

I haven't had time to create a benchmark script, but I would love to see how it compares with other available libraries.

Would you mind adding jjv to your list in your benchmark wiki? I believe it should be fairly easy to do, the following snippet should do the trick:

Tester.registerValidator({
  name: 'jjv',
  setup: function () {
    return jjv();
  },
  test: function (instance, json, schema) { 
    return instance.validate(schema, json) === null;
  }
})

The above assumes you did var jjv=require('jjv'); and that you added jjv 0.3.0 to the npm package list.

Thanks

use `id` attribute during compilation to avoid future remote lookups

I'm working on a project that is using JSON schema. I currently set the id attribute of my schema to the URI that they will be eventually deployed to, just like the upstream IETF draft schema: http://json-schema.org/draft-04/schema

Would it be possible to feed multiple schema into a single validator instance and have it use the id attributes to build a local map of available schema?

My hope is that by feeding enough local schema into a validator, I can avoid all remote lookups. I haven't actually deployed my new JSON schema yet, and the remote lookups are making testing somewhat inconvenient.

Proper way to reference schema files in same directory as root schema file?

Hi, I am working with schema files on a local filesystem, and am wondering how a schema file that is referenced should be entered in the main schema file?

example:

"schemas":{
  "schemaA":{
     "description":"description",
     "id":"schemaA",
     "type":"object",
     "properties":{
        "payments":{
           "type":"array",
           "description":"description",
           "items":{
              "$ref":"file-to-ref-same-directory.json"
           }
        }

What format should the value for $ref be if the json file is in the same directory as the primary json file?

Thanks!

work on/with z-schema

I am working on a factory-girl type thing based on json/schema with your library.

Currently I am keeping inin a branch of your z-schema library

https://github.com/leapmotion/z-schema/tree/factory

which also features class files. To do so I have added a method or two to your library and exposed some of its internal componentry.

If you feel this is better put as a seperate (factory) branch I can spin it off, but I'd still need to expose the schema digestion elements as thier own thing.

Can we talk?

Dave Edelhart
Leapmotion

Strict mode for email/domain names

The email format is pretty strict about checking for global Top-Level Domains:
(aero|arpa|biz|com|coop|edu|gov|info|int|mil|museum|name|net|org|pro|travel|mobi|[a-z][a-z])

The JSON schema spec is less strict about email, just saying they should comply with:
http://xml2rfc.ietf.org/public/rfc/html/rfc5322.html#addrspec

Although this seems useful the majority of the time, there are two problems with maintaining a strict list of gTLDs:

  • ICANN has been rapidly granting new gTLDs. According to ICANN:

The expansion of generic Top-Level Domains (e.g. .COM, .ORG, .NET) in the Domain Name System is underway. Over 1,300 new names or "strings" could become available in the next few years.
http://newgtlds.icann.org/en/program-status/delegated-strings

  • Emails addresses that are valid on intranets, pseudo-top-level-domains, or other networks that use TLDs that are not delegated by ICANN.

FYI: The email address I was testing was [email protected].

Use JSON pointer syntax for error path strings

Right now, the string path of an error is a mix of JSON Pointer and some JavaScript string you would eval. I would like to propose that error paths, when in string form (See #55), use JSON Pointer syntax. So instead of #/some/[1]/path[operation], it would be #/some/1/path/operation.

I ran across this while working on #55. If you'd like, I an submit a PR for this as well once the PR for #55 is completed.

Include description (if any) if property of type string with pattern fails

First of all, awesome library :) we are starting to use it and really like the robustness and how easy to use it is.

Now, onto the issue. If we have the following (partial) schema:

"name": {
  "type": "string",
  "pattern": "^[^<>]*$",
  "description": "Name of the client. Invalid characters are < and >."
},

It would be useful to get a human friendly representation of the error which is better that the regex. Currently the error looks like (in z-schema 2.x.x but is seems to provide the same info in 3.x.x):

{ code: 'PATTERN',
  message: 'String does not match pattern: ^[^<>]*$',
  path: '#/name',
  params: { pattern: '^[^<>]*$' } }

Ideally we would get:

{ code: 'PATTERN',
  message: 'String does not match pattern: ^[^<>]*$',
  path: '#/name',
  params: { pattern: '^[^<>]*$', description: 'Name of the client. Invalid characters are < and >.' } }

This seems to only require a small change at https://github.com/zaggino/z-schema/blob/master/src/JsonValidation.js#L77 but would be really useful for reporting errors back to users, e.g. when using JSON schema to validate API calls.

Thoughts?

Trailing slashes in schema id breaks $ref

I'm not sure if this is related to #44 but I used almost the same test to reproduce this issue. Long story short, if your schema id ends with a #, references to the schema by id result in an UNRESOLVABLE_REFERENCE:

var ZSchema = require('z-schema');
var validator = new ZSchema();
var schemaA = {
  id: 'schemaA#',
  type: 'string'
};
var schemaB = {
  id: 'schemaB#',
  properties: {
    a: {
      // '$ref': 'schemaA' // Fails
      '$ref': 'schemaA#' // Fails
    }
  }
};
var result = validator.compileSchema([schemaA, schemaB]);

if (result === false) {
  console.log(validator.getLastErrors());
}

The output of this is:

[ { code: 'UNRESOLVABLE_REFERENCE',
    params: [ 'schemaB#schemaA#' ],
    message: 'Reference could not be resolved: schemaB#schemaA#',
    path: '#/properties/a' } ]

If I remove the trailing # from the id, everything works fine:

var ZSchema = require('z-schema');
var validator = new ZSchema();
var schemaA = {
  id: 'schemaA',
  type: 'string'
};
var schemaB = {
  id: 'schemaB',
  properties: {
    a: {
      // '$ref': 'schemaA' // Works
      '$ref': 'schemaA#' // Works
    }
  }
};
var result = validator.compileSchema([schemaA, schemaB]);

if (result === false) {
  console.log(validator.getLastErrors());
}

How to register several schemas with an id?

I'm not sure if this question is already answered.

If I have 3 schemas in memory, each of them has an id attribute, one of them is the main schema.

How can I do the following with z-schema:

  1. register all 3 schemas with z-schema validator
  2. check if there are unresolved references
  3. validate objects against one of the 3 schemas

I can do this with other libraries but do not see how to do this with z-schema. setRemoteReference needs an url, which I'm not willing to provide because each of the schemas has an id attribute.

Here are the schemas, for example:

var schemaA = {id: "schemaA", type: "integer"};
var schemaB = {id: "schemaB", type: "string"};
var mainSchema = {
    id: "mainSchema",
    type: "object",
    properties: {
        a: {"$ref": "schemaA"},
        b: {"$ref": "schemaB"},
        c: {"enum": ["C"]}
    }
};

There is no validation function for format "regex"

Having a schema with $schema: 'http://json-schema.org/draft-04/schema#', setting remote schema

ZSchema.setRemoteReference('http://json-schema.org/draft-04/schema',
  fs.readFileSync (path.join __dirname, 'json-schema-draft-04', 'schema.json'), 'utf8')

and validate something against that schema, leads to

{
  code: 'UNKNOWN_FORMAT',
  message: 'There is no validation function for format "regex"',
  path: '#/properties[pattern]',
  params: { format: 'regex' } 
} 

Extremely long validation time for complex object

Okay, if I validate this object it takes <1 ms.

{
    firstname: 'First',
    lastname: 'Last',
}

If I validate this object, it takes 32ms. What is happening that the ObjectID adds 31 ms processing time? Processing time goes up linearly, the more ObjectID's it has to check. Sometimes I need to check arrays of objects with ObjectIDs and that gets outrageous.

{
    _id: new ObjectID(),  // mongodb id
    firstname: 'First',
    lastname: 'Last',
}

Unexpected error if a $ref cannot be resolved

Running the following code under Node.js

var ZSchema, main, schemaA;
ZSchema = require("z-schema");

schemaA = {
    $schema: 'http://json-schema.org/draft-04/schema#',
    id: "schemaA",
    type: "object",
    properties: {a: {type: "integer"}, b: {type: "string"}, c: {$ref: 'schemaB'}},
    required: ['a', 'c']
  };

var json, schema;
    schema = schemaA;
    json = {a: 4, c: 4};
    console.info("#1: validate ...");
    ZSchema.validate(json, schema, function(err, report) {
      var _ref;
      if (err) {
        console.error("... not valid!");
        if ("errors" in err) {
          console.error(err.errors);
        }
        return;
      }
      console.info("... OK");
      if ((_ref = report.warnings) != null ? _ref.length : void 0) {
        return console.warn(report.warnings);
      }
    });

results to the following console output, which I don't expect:

#1: validate ...
... not valid!
[ { code: 'INVALID_TYPE',
    message: 'invalid type: integer (expected object)',
    path: '#/c',
    params: { expected: 'object', type: 'integer' } } ]

References to draft4 subschema are not working

I've been trying to use Z-Schema to validate a schema for the Swagger 2 specification.

I'm getting this error:

Error: Use .setRemoteReference to download references in sync mode: http://json-schema.org/draft-04/schema

I've tried it as sync, async, and explicitly using setRemoteReference on a downloaded copy of the schema. None have worked.

I was able to get it to work by altering the host of the $ref (e.g. "json-schema.org" to "json-schema.orgx") and doing a setRemoteReference. So I'm speculating, but I think that the "json-schema.org" schemas are probably being treated specially and download is being skipped, but then can't be found when we refer to a subschema. Example:

"title": { "$ref": "http://json-schema.org/draft-04/schema#/properties/title" }

I created a gist with a frozen copy of the schema and sample you can try, or you can check out reverb/swagger-spec.

Partial validation & tying additional validations to partial validations

I'm still pretty new to JSON schema, but looking around at the other JSON Schema validation libraries out there, I have yet to find one that will support my use case. However judging by the z-schema API, it appears that you may be the most open to the ideas I have in mind were I to fork and submit pull requests.

Basically, I'm looking to add support for two features:

(1) the ability to register additional non-JSON-schema validations. For example, when validating an npm package.json file, it's easy to define a validation that checks if a string is a valid path. However, I would also like to be able to register a validation that checks if those paths actually exist on disk relative to the location of the package.json file itself. Another example would be checking if a homepage URL doesn't return a 404 not found error.

I reckon this feature would be implementable by allowing you to register pre and post validation functions that receive the value of the property being validated and returning true if additional validations succeed or errors otherwise. These functions would need access to the context in which they are called (so that they can get access via closures to details such as the directory in which a package.json exists).

(2) partial validation for performance reasons. For example, if I have already loaded a JSON into memory and validated it on load, I don't want to completely revalidate it on write. Instead I'd like the ability to only validate the properties that I am modifying.

validator.validate(partialJSON, "lib://package.json#/definitions/dependency-map", function(err, result) {
  if (err) { /* ..  handle err ... */ }
  /* ... add partialJSON to full JSON and write to disk ... */
});

or alternatively schemas could have method support for getting definitions, like so:

compiledSchema.getDefinition("dependency-map")

or a validatePartial method could be added to ZSchema like so:

validator.validate(partialJSON, schema, definitionName, function(err, result) {
  if (err) { /* ..  handle err ... */ }
  /* ... add partialJSON to full JSON and write to disk ... */
});

For this second feature the following features would be valuable:

As far as I can tell, suggestion two seems reasonable, but the first suggestion might be better as a separate module built on top of your validator or another one. However, to build feature (1) on top, it would be immensely useful to have partial validation.

I just started looking through the source and I'm still thinking through how these might be implementable, but I wanted to get your thoughts on them. Would you be open to accepting pull requests for these features?

Validate json by schema name

Hi, is there ability to cache schema by setRemoteReference and next - validate by it's uri? For example:

ZSchema.setRemoteReference('http://localhost:1234/integer.json', fileContent);
ZSchema.validate(myJsonData, 'http://localhost:1234/integer.json', function () {...})

Cheers

noTypeless behaviour

The compiler throws a schema validation error when noTypeless is set to true and instead of the type keyword any of the keywords oneOf, anyOf, not, $ref are present.

Is this the desired behaviour ? I think it would be more convenient if the error is not thrown if any one of those keywords are present on the schema since they all essentially define what the type of the object should be.

format 'hostname' doesn't work correctly

String '127.0.0.1' validates against format 'hostname' and 'ipv4' which is not correct!

{
  "ipAddress": "127.0.0.1"
}

must be ok against

{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "type": "object",
  "properties": {
    "ipAddress": {
      "type": "string",
      "oneOf": [
        {
          "format": "hostname"
        },
        {
          "format": "ipv4"
        },
        {
          "format": "ipv6"
        }
      ]
    }
  }
}

but is not with Z-Schema.

You can check with http://json-schema-validator.herokuapp.com/ or look at http://json-schema.org/example2.html. Also have a look at natesilva/jayschema#21.

Clarification on validating the URI format and the reason for strictUris mode.

Having a schema with $schema: 'http://json-schema.org/draft-04/schema#', setting remote schema

ZSchema.setRemoteReference('http://json-schema.org/draft-04/schema',
  fs.readFileSync (path.join __dirname, 'json-schema-draft-04', 'schema.json'), 'utf8')

and validate something against that schema, leads to

{
  code: 'FORMAT',
  message: 'uri format validation failed: schemaA',
  path: '#/id',
  params: { format: 'uri', error: 'schemaA' } 
}

If I remove $schema: 'http://json-schema.org/draft-04/schema#' from my schema validation is ok.

My schema is quite simple:

schemaA = {
    $schema: 'http://json-schema.org/draft-04/schema#',
    id: "schemaA",
    type: "object",
    properties: {
      a: {
        type: "integer"
      },
      b: {
        type: "string"
      }
    },
    required: ['a']
  };

If I change the id value from 'schemaA' to 'http://my.company.de/project#mainSchema' then validation is ok. Isn't 'schemaA' a valid URI ?

Maximum call stack exceeded when compiling array of schemas

I have an application that uses Swagger 1.2 schema files. Their schema files are not perfect but I've been able to work around them using jjv. I want to use z-schema and I've started writing a few tests to see how I'd load the schema files I need. I've run into a problem where a Maximum call stack exceeded happens with the following code (Warning, it's long but the purpose was to give you a copy/paste test instead of you having to download the Swagger 1.2 schema files yourself.). When I run the code, I get the following error:

/private/tmp/testing/node_modules/z-schema/src/SchemaCache.js:38
    if (Array.isArray(schema)) {
              ^
RangeError: Maximum call stack size exceeded

I'm using [email protected]. Here is the code that creates the error above:

var dataTypeBaseJson = {
  "id": "http://wordnik.github.io/schemas/v1.2/dataTypeBase.json#",
  "$schema": "http://json-schema.org/draft-04/schema#",
  "description": "Data type fields (section 4.3.3)",
  "type": "object",
  "oneOf": [
    {
      "required": [
        "type"
      ]
    },
    {
      "required": [
        "$ref"
      ]
    }
  ],
  "properties": {
    "type": {
      "type": "string"
    },
    "$ref": {
      "type": "string"
    },
    "format": {
      "type": "string"
    },
    "defaultValue": {
      "not": {
        "type": [
          "array",
          "object",
          "null"
        ]
      }
    },
    "enum": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "uniqueItems": true,
      "minItems": 1
    },
    "minimum": {
      "type": "string"
    },
    "maximum": {
      "type": "string"
    },
    "items": {
      "$ref": "#/definitions/itemsObject"
    },
    "uniqueItems": {
      "type": "boolean"
    }
  },
  "dependencies": {
    "format": {
      "oneOf": [
        {
          "properties": {
            "type": {
              "enum": [
                "integer"
              ]
            },
            "format": {
              "enum": [
                "int32",
                "int64"
              ]
            }
          }
        },
        {
          "properties": {
            "type": {
              "enum": [
                "number"
              ]
            },
            "format": {
              "enum": [
                "float",
                "double"
              ]
            }
          }
        },
        {
          "properties": {
            "type": {
              "enum": [
                "string"
              ]
            },
            "format": {
              "enum": [
                "byte",
                "date",
                "date-time"
              ]
            }
          }
        }
      ]
    }
  },
  "definitions": {
    "itemsObject": {
      "oneOf": [
        {
          "type": "object",
          "required": [
            "$ref"
          ],
          "properties": {
            "$ref": {
              "type": "string"
            }
          },
          "additionalProperties": false
        },
        {
          "allOf": [
            {
              "$ref": "#"
            },
            {
              "required": [
                "type"
              ],
              "properties": {
                "type": {},
                "format": {}
              },
              "additionalProperties": false
            }
          ]
        }
      ]
    }
  }
};
var modelsObjectJson = {
  "id": "http://wordnik.github.io/schemas/v1.2/modelsObject.json#",
  "$schema": "http://json-schema.org/draft-04/schema#",
  "type": "object",
  "required": [
    "id",
    "properties"
  ],
  "properties": {
    "id": {
      "type": "string"
    },
    "description": {
      "type": "string"
    },
    "properties": {
      "type": "object",
      "additionalProperties": {
        "$ref": "#/definitions/propertyObject"
      }
    },
    "subTypes": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "uniqueItems": true
    },
    "discriminator": {
      "type": "string"
    }
  },
  "dependencies": {
    "subTypes": [
      "discriminator"
    ]
  },
  "definitions": {
    "propertyObject": {
      "allOf": [
        {
          "not": {
            "$ref": "#"
          }
        },
        {
          "$ref": "dataTypeBase.json#"
        }
      ]
    }
  }
};
var oauth2GrantTypeJson = {
  "id": "http://wordnik.github.io/schemas/v1.2/oauth2GrantType.json#",
  "$schema": "http://json-schema.org/draft-04/schema#",
  "type": "object",
  "minProperties": 1,
  "properties": {
    "implicit": {
      "$ref": "#/definitions/implicit"
    },
    "authorization_code": {
      "$ref": "#/definitions/authorizationCode"
    }
  },
  "definitions": {
    "implicit": {
      "type": "object",
      "required": [
        "loginEndpoint"
      ],
      "properties": {
        "loginEndpoint": {
          "$ref": "#/definitions/loginEndpoint"
        },
        "tokenName": {
          "type": "string"
        }
      },
      "additionalProperties": false
    },
    "authorizationCode": {
      "type": "object",
      "required": [
        "tokenEndpoint",
        "tokenRequestEndpoint"
      ],
      "properties": {
        "tokenEndpoint": {
          "$ref": "#/definitions/tokenEndpoint"
        },
        "tokenRequestEndpoint": {
          "$ref": "#/definitions/tokenRequestEndpoint"
        }
      },
      "additionalProperties": false
    },
    "loginEndpoint": {
      "type": "object",
      "required": [
        "url"
      ],
      "properties": {
        "url": {
          "type": "string",
          "format": "uri"
        }
      },
      "additionalProperties": false
    },
    "tokenEndpoint": {
      "type": "object",
      "required": [
        "url"
      ],
      "properties": {
        "url": {
          "type": "string",
          "format": "uri"
        },
        "tokenName": {
          "type": "string"
        }
      },
      "additionalProperties": false
    },
    "tokenRequestEndpoint": {
      "type": "object",
      "required": [
        "url"
      ],
      "properties": {
        "url": {
          "type": "string",
          "format": "uri"
        },
        "clientIdName": {
          "type": "string"
        },
        "clientSecretName": {
          "type": "string"
        }
      },
      "additionalProperties": false
    }
  }
};
var authorizationObjectJson = {
  "id": "http://wordnik.github.io/schemas/v1.2/authorizationObject.json#",
  "$schema": "http://json-schema.org/draft-04/schema#",
  "type": "object",
  "additionalProperties": {
    "oneOf": [
      {
        "$ref": "#/definitions/basicAuth"
      },
      {
        "$ref": "#/definitions/apiKey"
      },
      {
        "$ref": "#/definitions/oauth2"
      }
    ]
  },
  "definitions": {
    "basicAuth": {
      "required": [
        "type"
      ],
      "properties": {
        "type": {
          "enum": [
            "basicAuth"
          ]
        }
      },
      "additionalProperties": false
    },
    "apiKey": {
      "required": [
        "type",
        "passAs",
        "keyname"
      ],
      "properties": {
        "type": {
          "enum": [
            "apiKey"
          ]
        },
        "passAs": {
          "enum": [
            "header",
            "query"
          ]
        },
        "keyname": {
          "type": "string"
        }
      },
      "additionalProperties": false
    },
    "oauth2": {
      "type": "object",
      "required": [
        "type",
        "grantTypes"
      ],
      "properties": {
        "type": {
          "enum": [
            "oauth2"
          ]
        },
        "scopes": {
          "type": "array",
          "items": {
            "$ref": "#/definitions/oauth2Scope"
          }
        },
        "grantTypes": {
          "$ref": "oauth2GrantType.json#"
        }
      },
      "additionalProperties": false
    },
    "oauth2Scope": {
      "type": "object",
      "required": [
        "scope"
      ],
      "properties": {
        "scope": {
          "type": "string"
        },
        "description": {
          "type": "string"
        }
      },
      "additionalProperties": false
    }
  }
};
var parameterObjectJson = {
  "id": "http://wordnik.github.io/schemas/v1.2/parameterObject.json#",
  "$schema": "http://json-schema.org/draft-04/schema#",
  "type": "object",
  "allOf": [
    {
      "$ref": "dataTypeBase.json#"
    },
    {
      "required": [
        "paramType",
        "name"
      ],
      "properties": {
        "paramType": {
          "enum": [
            "path",
            "query",
            "body",
            "header",
            "form"
          ]
        },
        "name": {
          "type": "string"
        },
        "description": {
          "type": "string"
        },
        "required": {
          "type": "boolean"
        },
        "allowMultiple": {
          "type": "boolean"
        }
      }
    },
    {
      "description": "type File requires special paramType and consumes",
      "oneOf": [
        {
          "properties": {
            "type": {
              "not": {
                "enum": [
                  "File"
                ]
              }
            }
          }
        },
        {
          "properties": {
            "type": {
              "enum": [
                "File"
              ]
            },
            "paramType": {
              "enum": [
                "form"
              ]
            },
            "consumes": {
              "enum": [
                "multipart/form-data"
              ]
            }
          }
        }
      ]
    }
  ]
};
var operationObjectJson = {
  "id": "http://wordnik.github.io/schemas/v1.2/operationObject.json#",
  "$schema": "http://json-schema.org/draft-04/schema#",
  "type": "object",
  "allOf": [
    {
      "$ref": "dataTypeBase.json#"
    },
    {
      "required": [
        "method",
        "nickname",
        "parameters"
      ],
      "properties": {
        "method": {
          "enum": [
            "GET",
            "POST",
            "PUT",
            "PATCH",
            "DELETE",
            "OPTIONS"
          ]
        },
        "summary": {
          "type": "string",
          "maxLength": 120
        },
        "notes": {
          "type": "string"
        },
        "nickname": {
          "type": "string",
          "pattern": "^[a-zA-Z0-9_]+$"
        },
        "authorizations": {
          "type": "object",
          "additionalProperties": {
            "type": "array",
            "items": {
              "$ref": "authorizationObject.json#/definitions/oauth2Scope"
            }
          }
        },
        "parameters": {
          "type": "array",
          "items": {
            "$ref": "parameterObject.json#"
          }
        },
        "responseMessages": {
          "type": "array",
          "items": {
            "$ref": "#/definitions/responseMessageObject"
          }
        },
        "produces": {
          "$ref": "#/definitions/mimeTypeArray"
        },
        "consumes": {
          "$ref": "#/definitions/mimeTypeArray"
        },
        "deprecated": {
          "enum": [
            "true",
            "false"
          ]
        }
      }
    }
  ],
  "definitions": {
    "responseMessageObject": {
      "type": "object",
      "required": [
        "code",
        "message"
      ],
      "properties": {
        "code": {
          "$ref": "#/definitions/rfc2616section10"
        },
        "message": {
          "type": "string"
        },
        "responseModel": {
          "type": "string"
        }
      }
    },
    "rfc2616section10": {
      "type": "integer",
      "minimum": 100,
      "maximum": 600,
      "exclusiveMaximum": true
    },
    "mimeTypeArray": {
      "type": "array",
      "items": {
        "type": "string",
        "format": "mime-type"
      }
    }
  }
}
var apiDeclarationJson = {
  "id": "http://wordnik.github.io/schemas/v1.2/apiDeclaration.json#",
  "$schema": "http://json-schema.org/draft-04/schema#",
  "type": "object",
  "required": [
    "swaggerVersion",
    "basePath",
    "apis"
  ],
  "properties": {
    "swaggerVersion": {
      "enum": [
        "1.2"
      ]
    },
    "apiVersion": {
      "type": "string"
    },
    "basePath": {
      "type": "string",
      "format": "uri",
      "pattern": "^https?://"
    },
    "resourcePath": {
      "type": "string",
      "format": "uri",
      "pattern": "^/"
    },
    "apis": {
      "type": "array",
      "items": {
        "$ref": "#/definitions/apiObject"
      }
    },
    "models": {
      "type": "object",
      "additionalProperties": {
        "$ref": "modelsObject.json#"
      }
    },
    "produces": {
      "$ref": "#/definitions/mimeTypeArray"
    },
    "consumes": {
      "$ref": "#/definitions/mimeTypeArray"
    },
    "authorizations": {
      "$ref": "authorizationObject.json#"
    }
  },
  "additionalProperties": false,
  "definitions": {
    "apiObject": {
      "type": "object",
      "required": [
        "path",
        "operations"
      ],
      "properties": {
        "path": {
          "type": "string",
          "format": "uri-template",
          "pattern": "^/"
        },
        "description": {
          "type": "string"
        },
        "operations": {
          "type": "array",
          "items": {
            "$ref": "operationObject.json#"
          }
        }
      },
      "additionalProperties": false
    },
    "mimeTypeArray": {
      "type": "array",
      "items": {
        "type": "string",
        "format": "mime-type"
      }
    }
  }
};
var ZSchema = require('z-schema');
var validator = new ZSchema();
var result = validator.compileSchema([
  dataTypeBaseJson,
  modelsObjectJson,
  oauth2GrantTypeJson,
  authorizationObjectJson,
  parameterObjectJson,
  operationObjectJson,
  apiDeclarationJson
]);

if (result === false) {
  console.dir(validator.getLastErrors());
}

"$schema" keyword fails validation

Steps to reproduce:

  1. start node REPL
  2. execute the following code:
var schema = {
  "$schema": "http://json-schema.org/schema#",
  "properties": { "text": { "type": "string" } },
  "type": "object"
};
var json = { "text": "o"};
require("z-schema").validate(json, schema, console.log);

Expected:

Successful validation.

Actual:

.../node_modules/z-schema/src/ZSchema.js:318
                    throw new Error('Not a JSON data at: ' + url + ', ' + e);
                          ^
Error: Not a JSON data at: http://json-schema.org/schema, SyntaxError: Unexpected end of input
    at returnSchemaFromString (/Users/abj/Documents/LoveThis/git/antigua_api/service-dym/test/node_modules/z-schema/src/ZSchema.js:318:27)
    at IncomingMessage.<anonymous> (/Users/abj/Documents/LoveThis/git/antigua_api/service-dym/test/node_modules/z-schema/src/ZSchema.js:344:21)
    at IncomingMessage.EventEmitter.emit (events.js:117:20)
    at _stream_readable.js:920:16
    at process._tickCallback (node.js:415:13)

Reference:

Validation for optional objects

First of all, thank you for a great library. I've found it really useful and its 'options' features is the one which I could not find in other libraries.

I was working with different schema validations for a project. Let's say, my whole JSON body is empty and the schema defines all the fields are optional.

  1. Is there a way to define that even though all the fields are optional, at least one (any of it) should be present in the body and the body should not be empty?
  2. If my JSON body has two fields A and B and the schema defines A and B as optional. Is there a way to define that if A is present, then, B is required?

It may not be a bug, but I could find the documentation supporting such features.

Thank you for your time.

Neeraj

'Maximum call stack size exceeded' for self-referencing allOf+not

I'm trying to get z-schema to load the Swagger Schemas. I noticed that for the modelsObject.json, I was getting a Maximum call stack size exceeded. Below is a recipe that will reproduce the issue. schemaA corresponds to dataTypeBase.json and schemaB corresponds with modelsObject.json.

var schemaA = {
  'id': 'schemaA',
  '$schema': 'http://json-schema.org/draft-04/schema#',
  'description': 'Data type fields (section 4.3.3)',
  'type': 'object',
  'oneOf': [
    { 'required': [ 'type' ] },
    { 'required': [ '$ref' ] }
  ],
  'properties': {
    'type': { 'type': 'string' },
    '$ref': { 'type': 'string' },
    'format': { 'type': 'string' },
    'defaultValue': {
      'not': { 'type': [ 'array', 'object', 'null' ] }
    },
    'enum': {
      'type': 'array',
      'items': { 'type': 'string' },
      'uniqueItems': true,
      'minItems': 1
    },
    'minimum': { 'type': 'string' },
    'maximum': { 'type': 'string' },
    'items': { '$ref': '#/definitions/itemsObject' },
    'uniqueItems': { 'type': 'boolean' }
  },
  'dependencies': {
    'format': {
      'oneOf': [
        {
          'properties': {
            'type': { 'enum': [ 'integer' ] },
            'format': { 'enum': [ 'int32', 'int64' ] }
          }
        },
        {
          'properties': {
            'type': { 'enum': [ 'number' ] },
            'format': { 'enum': [ 'float', 'double' ] }
          }
        },
        {
          'properties': {
            'type': { 'enum': [ 'string' ] },
            'format': {
              'enum': [ 'byte', 'date', 'date-time' ]
            }
          }
        }
      ]
    }
  },
  'definitions': {
    'itemsObject': {
      'oneOf': [
        {
          'type': 'object',
          'required': [ '$ref' ],
          'properties': {
            '$ref': { 'type': 'string' }
          },
          'additionalProperties': false
        },
        {
          'allOf': [
            { '$ref': '#' },
            {
              'required': [ 'type' ],
              'properties': {
                'type': {},
                'format': {}
              },
              'additionalProperties': false
            }
          ]
        }
      ]
    }
  }
};
var schemaB = {
  'id': 'schemaB',
  '$schema': 'http://json-schema.org/draft-04/schema#',
  'type': 'object',
  'required': [ 'id', 'properties' ],
  'properties': {
    'id': { 'type': 'string' },
    'description': { 'type': 'string' },
    'properties': {
      'type': 'object',
      'additionalProperties': { '$ref': '#/definitions/propertyObject' }
    },
    'subTypes': {
      'type': 'array',
      'items': { 'type': 'string' },
      'uniqueItems': true
    },
    'discriminator': { 'type': 'string' }
  },
  'dependencies': {
    'subTypes': [ 'discriminator' ]
  },
  'definitions': {
    'propertyObject': {
      'allOf': [
        {
          'not': { '$ref': '#' }
        },
        {
          '$ref': 'schemaA'
        }
      ]
    }
  }
};
var ZSchema = require('z-schema');
var validator = new ZSchema({sync: true});

try {
  validator.compileSchema([schemaA, schemaB]);
} catch (err) {
  var vError = validator.getLastError();

  if (vError && vError.valid === false) {
    console.log(validator.getLastError());
  } else {
    throw err;
  }
}

expose schema validation methods

I've noticed that schema are automatically validated when you attempt to use them to validate data.

I'm writing unit tests for a project that uses JSON schema, and it would be terrific if I could access these validation methods easily to facilitate with my tests.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.