Skip to content

Instantly share code, notes, and snippets.

@bullno1
Created July 28, 2012 14:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bullno1/3193629 to your computer and use it in GitHub Desktop.
Save bullno1/3193629 to your computer and use it in GitHub Desktop.
JSON schema validation
number = function(x) { return getType(x) == 'number'; }
string = function(x) { return getType(x) == 'string'; }
//combinators
function or() {
var len = arguments.length;
var args = arguments;
return function(x) {
for(var i = 0; i < len; ++i) {
if(validate(args[i], x)) return true;//satisfy at least one condition
}
return false;
}
}
function and() {
var len = arguments.length;
var args = arguments;
return function(x) {
for(var i = 0; i < len; ++i) {
if(!validate(args[i], x)) return false;//fail at least one condition
}
return true;
}
}
function not(t) {
return function(x) {
return !validate(t, x);
};
}
function Wrapper() {
this.innerSchema = null;
Object.defineProperty(this, "_", {
get: function() { return this.innerSchema; },
set: function(value) { this.innerSchema = value; }
});
}
function rec() { return new Wrapper(); }
//comparision
function gt(rhs) {
return function(x) { return x > rhs; };
}
function lt(rhs) {
return function(x) { return x < rhs; };
}
//Some useful types
function optional(t) {
return or(undefined, t);
}
var validator;
function validate(t, x) {
return validator[getType(t)](t, x);
}
validator = {
//primitive literals must be exactly matched
'number': equal,
'string': equal,
'null': equal,
'undefined': equal,
//a function validator
'function': function(t, x) {
return t(x);
},
//type wrapper
'wrapper': function(wrapper, x) {
return validate(wrapper.innerSchema, x);
},
//an array of type t
'array': function(arr, x) {
if(getType(x) !== 'array') return false;
var t = arr[0];
var len = x.length;
for(var i = 0; i < len; ++i) {
if(!validate(t, x[i])) return false;
}
return true;
},
//a dict schema
'dict': function(dict, x) {
if(getType(x) !== 'dict') return false;
for(var key in dict) {
var value = x[key];
var valueT = dict[key];
if(!validate(valueT, value)) return false;
}
return true;
}
}
function equal(lhs, rhs) { return lhs === rhs; }
function getType(x) {
var nativeType = typeof(x);
if(nativeType === 'number') return 'number';
if(nativeType === 'string') return 'string';
if(nativeType === 'function') return 'function';
if(x === null) return 'null';
if(x === undefined) return 'undefined';
if(x instanceof Array) return 'array';
if(x instanceof Wrapper) return 'wrapper';
return 'dict';
}
//-------------------------------------------------------
non_neg = and(number, or(0, gt(0)));//a number that is 0 or > 0
var item = {
"id": number,
"username": string,
"numPosts": non_neg,//non negative number
"realName": optional(string)
};
var doc = {
"users": [item]
};
var testDoc = {
"users": [
{
"id": 2,
"username": "A",
"numPosts": 2
},
{
"id": 4,
"username": "B",
"numPosts": 0,
"realName": "BBBB"
}
]
};
console.log(validate(doc, testDoc));//true
var binaryTree = rec();
binaryTree._ = or(
null,
{
"left": optional(binaryTree),
"right": optional(binaryTree)
}
);
var testTree = {
"right": {
"left": {
"left": null,
"right": null,
},
}
}
var testTree2 = {
"left": 1,
"right": {
"left": {
"left": null,
"right": null,
},
}
}
console.log(validate(binaryTree, testTree));//true
console.log(validate(binaryTree, testTree2));//false
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment