Reputation: 2558
I have defined object with nested properties. I want to create a validator function which will check if another object has the same structure and value type as the one that I have defined!
The is the definition of the object:
const OBJECT_SCHEMA = {
name: String,
data: [{
isSelected: Boolean,
mId: String,
mSummary: String,
mMarkets: Array,
mBdd: String,
mReplaceDict: Object,
omId: String,
omnSummary: String,
omnMarkets: Array,
omnBdd: String,
omnReplaceDict: {
id: String,
text: String,
},
}],
metadata: {
emails: Array,
description: String,
},
};
And here is the function that I have for validation. Currently it works only with one nested level! I want it to validate with many nested levels.
function validateObjectStructure(schema, obj) {
let valid = true;
firstLevel: for(const k in schema) {
if(schema[k].constructor === Array) { // if prop is of type array
let i;
for(i = 0; i < schema[k].length; i++) {
for(const kk in schema[k][i]) {
if(!obj[k][i].hasOwnProperty(kk) || obj[k][i][kk].constructor !== schema[k][i][kk]) {
valid = false;
break firstLevel;
}
}
}
}
else if(schema[k].constructor === Object) { // if prop is of type object
for(const kk in schema[k]) {
if(!obj[k].hasOwnProperty(kk) || obj[k][kk].constructor !== schema[k][kk]) {
valid = false;
break firstLevel;
}
}
}
else { // if prop is simple type
if(!obj.hasOwnProperty(k) || obj[k].constructor !== schema[k]) {
valid = false;
break;
}
}
}
return valid;
}
Upvotes: 2
Views: 3757
Reputation: 215009
Here's a possible implementation:
function validate(obj, schema, path = '') {
let ok = true;
if (!obj)
ok = obj === schema;
else if (typeof schema === 'function')
ok = obj.constructor === schema;
else if (typeof obj !== 'object')
ok = obj === schema;
else if (Array.isArray(schema))
ok = Array.isArray(obj) && obj.every((x, k) => validate(x, schema[0], path + '[' + k + ']'));
else {
let ko = Object.keys(obj);
let ks = Object.keys(schema);
ok = ko.length === ks.length && ks.every(k => validate(obj[k], schema[k], path + '.' + k));
}
if (!ok)
throw new Error('FAILED ' + path);
return true;
}
// example:
const OBJECT_SCHEMA = {
name: String,
data: [{
isSelected: Boolean,
mId: String,
omnReplaceDict: {
id: String,
text: {
deepObj: {
deepProp: [Number]
}
},
},
}],
};
const obj = {
name: "foo",
data: [{
isSelected: true,
mId: "bar",
omnReplaceDict: {
id: "foo",
text: {
deepObj: {
deepProp: [1, 2, "???", 3]
}
},
},
}]
};
validate(obj, OBJECT_SCHEMA)
Note: although this home-made type checker appears to work correctly, it's quite limited (e.g. how to express "array of string-number pairs" or "either null or some object"?), so it might be an option to employ a real one, like Typescript. See here for a possible implementation.
Upvotes: 1
Reputation: 353
Do you need to work with nested levels of the obj
? If yes, you can do something like this instead of the last line:
Object.values(obj).reduce((accValid, value) => {
if (typeof value === 'object') {
return accValid && validateObjectStructure(schema, value);
}
return accValid;
}, valid);
return valid;
Upvotes: 1