Check Integrity of Complex JavaScript Object - javascript

What is the best way to test the integrity of a complex object in JavaScript?
My object has a bunch of different variables, some optional, some required. The correct structure is vital to the code's functionality, but if I make a mistake during the definition, finding the exact value that caused the problem could get very tedious. Especially with error messages that tell me no more than "Somwehere in the code you're using the wrong variable type!".
My object could look something like this, for example:
{
name:"Test",
categories:{
A:{
depth:1,
groups:{
main:[
{myFunction:funcA, arg:[1,2]},
{myFunction:funcB}
]
}
},
B:{
groups{
main:[
{myFunction:funcC}
],
secondary:[
{myFunction:funcD}
]
}
}
}
}
Thanks!

There isn't a good way to do this beyond writing a function that receives an object as input, and verifies that it has the "right" structure.
function isValid(obj)
{
if (!o) return false;
if (typeof o.name !== 'string') return false;
if (typeof o.categories !== 'object') return false;
if (typeof o.categories.a !== 'object') return false;
if (typeof o.categories.b !== 'object') return false;
// etc...
return true;
}
On the other hand, you can define a constructor which takes whatever arguments you need to construct the object properly.
function MyObj(name, categoryNames /* other args */)
{
this.name = name;
this.categories = {};
for (var i=0; i<categoryNames.length; i++)
{
this.categories[categoryNames[i]] =
{
groups: {main: []}
};
}
// etc
}
// use it like this:
var foo = new MyObj('Test', ['A', 'B'] /* other args */);

Okay, this is how I've solved it: I create an object that defines what my complex objects should look like. A blueprint, in a way.
var structure = {
property1: {id:"name", type:"string", required:false},
property2: {
id:"categories", type:"object", required:true,
content:{
property1:{
type:"object", min:1,
content:{
property1:{id:"depth", type:"positiveinteger", required:false},
property2:{
id:"groups", type:"object", required:true,
content:{
property1:{
type:"array", min:1,
content:{
type:"object", min:1,
content:{
property1:{id:"myFunction", type:"function", required:true},
property2:{id:"args", type:"array", required:false},
}
}
}
}
}
}
}
}
}
}
Then I run a function that compares the blueprint ("struct") with the object to be tested ("def"):
function compareStructure(struct, def){
if(isArray(def)){
if(isDefined(struct.min) && def.length < struct.min){ alert("Error in structur check: " + " min is " + struct.min + "."); return false}
if(isDefined(struct.max) && def.length > struct.max){ alert("Error in structur check: " + " max is " + struct.max + "."); return false}
for(var i = 0; i < def.length; i++){
compareStructure(struct.content, def[i]);
}
} else {
for(var k in struct){
if(struct[k].id){
propFound = false;
for(var m in def) if(m == struct[k].id) propFound = m;
if(!propFound && struct[k].required){ alert("Error in structure check: " + struct[k].id + " not defined."); return false}
if(propFound && !verifyThis(struct[k], def[propFound], propFound)) return false;
if(propFound && struct[k].content) compareStructure(struct[k].content, def[propFound]);
} else {
for(var m in def){
if(!verifyThis(struct[k], def[m], m)) return false;
if(struct[k].content) compareStructure(struct[k].content, def[m]);
}
}
}
}
}
function verifyThis(struct, def, prop){
// This is where the checks for types and values are made.
}
The comparison function is still work in progress, but that's the concept I'm going with for now.

You can try to validate your object against a JSON Schema
But this may be an overkill.

You might be try JSON Schema Validation. You might need some modifications to account for the fact that you can have functions as well which are not valid in JSON.

Related

Check if nested JSON structure contains key

I'm trying to figure out how to check if a deeply nested JSON object, with several unknown arrays and properties contains a property that I'm looking for. I'm looking for a property that is called "isInvalid". If the field is there and the value of that key is true. I want to return false.
var checkValidity = function (data) {
for (var property in data) {
if (data.hasOwnProperty(property)) {
if (property == "isInvalid" && data[property] === true) {
return false;
}
else {
if (typeof data[property] === "object" && data[property] !== null) {
this.checkValidity(data[property]);
}
}
}
}
};
This is the code I've been trying out but I'm unable to get that to work. I have been looking into underscore also, but cant find the needed functions. Anyone has an idea? (No reg exp please)
If you really just want to check for property presence regardless of its particular location within JSON, then the easiest/fastest way is substring search in the source JSON string. If the latter is well-formed, then the property should be encoded in JSON as '"isInvalid":true'.
var checkValidity = function (jsonstr) {
return jsonstr.indexOf('"isInvalid":true') >= 0;
}
You can check like this
var s = {a:'1',b:'2'};
if(Object.getOwnPropertyNames(s).indexOf('a') != -1){
console.log('available');
}else{
console.log('Not available');
};
editing answer... UPDATE
var s = {
a1: '1',
b: '2',
c: {
a: '11'
}
};
var checkValidity = function (data) {
if (Object.getOwnPropertyNames(data).indexOf('a') != - 1) {
console.log('Found that key!!!');
} else {
for (var property in data) {
if (Object.getOwnPropertyNames(property).indexOf('a') != - 1) {
console.log('Found that key!!!');
} else {
if (typeof data[property] === 'object' && data[property] !== null) {
console.log('not found continue in inner obj..');
this.checkValidity(data[property]);
}
}
}
};
};
checkValidity(s);
It tests for every nesting level the property isInvalid and if not, all other properties as object and their content. Array#every breaks if one return is false.
function checkValidity(data) {
return !data.isInvalid && Object.keys(data).every(function (property) {
if (typeof data[property] === "object" && data[property] !== null) {
return checkValidity(data[property]);
}
return true;
});
}
var data = {
a: 1,
b: 2,
c: {
isInvalid: true,
a: false
}
};
document.write('checkValidity() should be false: ' + checkValidity(data) + '<br>');
data.c.isInvalid = false;
document.write('checkValidity() should be true: ' + checkValidity(data));
For complex json searching like this, I would use jsonpath ( http://goessner.net/articles/JsonPath/ ) which is the JSON equivalent of xpath.
To find the isInvalid field no matter where it is in the json, you would use it like this:
jsonPath(data, "$..isInvalid")

Object has-property-deep check in JavaScript

Let's say we have this JavaScript object:
var object = {
innerObject:{
deepObject:{
value:'Here am I'
}
}
};
How can we check if value property exists?
I can see only two ways:
First one:
if(object && object.innerObject && object.innerObject.deepObject && object.innerObject.deepObject.value) {
console.log('We found it!');
}
Second one:
if(object.hasOwnProperty('innerObject') && object.innerObject.hasOwnProperty('deepObject') && object.innerObject.deepObject.hasOwnProperty('value')) {
console.log('We found it too!');
}
But is there a way to do a deep check? Let's say, something like:
object['innerObject.deepObject.value']
or
object.hasOwnProperty('innerObject.deepObject.value')
There isn't a built-in way for this kind of check, but you can implement it easily. Create a function, pass a string representing the property path, split the path by ., and iterate over this path:
Object.prototype.hasOwnNestedProperty = function(propertyPath) {
if (!propertyPath)
return false;
var properties = propertyPath.split('.');
var obj = this;
for (var i = 0; i < properties.length; i++) {
var prop = properties[i];
if (!obj || !obj.hasOwnProperty(prop)) {
return false;
} else {
obj = obj[prop];
}
}
return true;
};
// Usage:
var obj = {
innerObject: {
deepObject: {
value: 'Here am I'
}
}
}
console.log(obj.hasOwnNestedProperty('innerObject.deepObject.value'));
You could make a recursive method to do this.
The method would iterate (recursively) on all 'object' properties of the object you pass in and return true as soon as it finds one that contains the property you pass in. If no object contains such property, it returns false.
var obj = {
innerObject: {
deepObject: {
value: 'Here am I'
}
}
};
function hasOwnDeepProperty(obj, prop) {
if (typeof obj === 'object' && obj !== null) { // only performs property checks on objects (taking care of the corner case for null as well)
if (obj.hasOwnProperty(prop)) { // if this object already contains the property, we are done
return true;
}
for (var p in obj) { // otherwise iterate on all the properties of this object.
if (obj.hasOwnProperty(p) && // and as soon as you find the property you are looking for, return true
hasOwnDeepProperty(obj[p], prop)) {
return true;
}
}
}
return false;
}
console.log(hasOwnDeepProperty(obj, 'value')); // true
console.log(hasOwnDeepProperty(obj, 'another')); // false
Alternative recursive function:
Loops over all object keys. For any key it checks if it is an object, and if so, calls itself recursively.
Otherwise, it returns an array with true, false, false for any key with the name propName.
The .reduce then rolls up the array through an or statement.
function deepCheck(obj,propName) {
if obj.hasOwnProperty(propName) { // Performance improvement (thanks to #nem's solution)
return true;
}
return Object.keys(obj) // Turns keys of object into array of strings
.map(prop => { // Loop over the array
if (typeof obj[prop] == 'object') { // If property is object,
return deepCheck(obj[prop],propName); // call recursively
} else {
return (prop == propName); // Return true or false
}
}) // The result is an array like [false, false, true, false]
.reduce(function(previousValue, currentValue, index, array) {
return previousValue || currentValue;
} // Do an 'or', or comparison of everything in the array.
// It returns true if at least one value is true.
)
}
deepCheck(object,'value'); // === true
PS: nem035's answer showed how it could be more performant: his solution breaks off at the first found 'value.'
My approach would be using try/catch blocks. Because I don't like to pass deep property paths in strings. I'm a lazy guy who likes autocompletion :)
JavaScript objects are evaluated on runtime. So if you return your object statement in a callback function, that statement is not going to be evaluated until callback function is invoked.
So this function just wraps the callback function inside a try catch statement. If it catches the exception returns false.
var obj = {
innerObject: {
deepObject: {
value: 'Here am I'
}
}
};
const validate = (cb) => {
try {
return cb();
} catch (e) {
return false;
}
}
if (validate(() => obj.innerObject.deepObject.value)) {
// Is going to work
}
if (validate(() => obj.x.y.z)) {
// Is not going to work
}
When it comes to performance, it's hard to say which approach is better.
On my tests if the object properties exist and the statement is successful I noticed using try/catch can be 2x 3x times faster than splitting string to keys and checking if keys exist in the object.
But if the property doesn't exist at some point, prototype approach returns the result almost 7x times faster.
See the test yourself: https://jsfiddle.net/yatki/382qoy13/2/
You can also check the library I wrote here: https://github.com/yatki/try-to-validate
I use try-catch:
var object = {
innerObject:{
deepObject:{
value:'Here am I'
}
}
};
var object2 = {
a: 10
}
let exist = false, exist2 = false;
try {
exist = !!object.innerObject.deepObject.value
exist2 = !!object2.innerObject.deepObject.value
}
catch(e) {
}
console.log(exist);
console.log(exist2);
Try this nice and easy solution:
public hasOwnDeepProperty(obj, path)
{
for (var i = 0, path = path.split('.'), len = path.length; i < len; i++)
{
obj = obj[path[i]];
if (!obj) return false;
};
return true;
}
In case you are writing JavaScript for Node.js, then there is an assert module with a 'deepEqual' method:
const assert = require('assert');
assert.deepEqual(testedObject, {
innerObject:{
deepObject:{
value:'Here am I'
}
}
});
I have created a very simple function for this using the recursive and happy flow coding strategy. It is also nice to add it to the Object.prototype (with enumerate:false!!) in order to have it available for all objects.
function objectHasOwnNestedProperty(obj, keys)
{
if (!obj || typeof obj !== 'object')
{
return false;
}
if(typeof keys === 'string')
{
keys = keys.split('.');
}
if(!Array.isArray(keys))
{
return false;
}
if(keys.length == 0)
{
return Object.keys(obj).length > 0;
}
var first_key = keys.shift();
if(!obj.hasOwnProperty(first_key))
{
return false;
}
if(keys.length == 0)
{
return true;
}
return objectHasOwnNestedProperty(obj[first_key],keys);
}
Object.defineProperty(Object.prototype, 'hasOwnNestedProperty',
{
value: function () { return objectHasOwnNestedProperty(this, ...arguments); },
enumerable: false
});

iterating through object

I'm having a really hard time trying to find a way to iterate through this object in the way that I'd like. I'm using only Javascript here.
First, here's the object
{
"dialog":
{
"dialog_trunk_1":{
"message": "This is just a JSON Test"
},
"dialog_trunk_2":{
"message": "and a test of the second message"
},
"dialog_trunk_3":
{
"message": "This is a test of a bit longer text. Hopefully this will at the very least create 3 lines and trigger us to go on to another box. So we can test multi-box functionality, too."
}
}
}
Right now, I'm just trying basic ways to get through to each dialog_trunk on this object. I ideally want to loop through the object and for each trunk, display it's message value.
I've tried using a for loop to generate the name/number of the dialog_trunk on the fly, but I can't access the object using a string for the object name so I'm not sure where to go from here.
You use a for..in loop for this. Be sure to check if the object owns the properties or all inherited properties are shown as well. An example is like this:
var obj = {a: 1, b: 2};
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
var val = obj[key];
console.log(val);
}
}
Or if you need recursion to walk through all the properties:
var obj = {a: 1, b: 2, c: {a: 1, b: 2}};
function walk(obj) {
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
var val = obj[key];
console.log(val);
walk(val);
}
}
}
walk(obj);
My problem was actually a problem of bad planning with the JSON object rather than an actual logic issue. What I ended up doing was organize the object as follows, per a suggestion from user2736012.
{
"dialog":
{
"trunks":[
{
"trunk_id" : "1",
"message": "This is just a JSON Test"
},
{
"trunk_id" : "2",
"message": "This is a test of a bit longer text. Hopefully this will at the very least create 3 lines and trigger us to go on to another box. So we can test multi-box functionality, too."
}
]
}
}
At that point, I was able to do a fairly simple for loop based on the total number of objects.
var totalMessages = Object.keys(messages.dialog.trunks).length;
for ( var i = 0; i < totalMessages; i++)
{
console.log("ID: " + messages.dialog.trunks[i].trunk_id + " Message " + messages.dialog.trunks[i].message);
}
My method for getting totalMessages is not supported in all browsers, though. For my project, it actually doesn't matter, but beware of that if you choose to use something similar to this.
Here is my recursive approach:
function visit(object) {
if (isIterable(object)) {
forEachIn(object, function (accessor, child) {
visit(child);
});
}
else {
var value = object;
console.log(value);
}
}
function forEachIn(iterable, functionRef) {
for (var accessor in iterable) {
functionRef(accessor, iterable[accessor]);
}
}
function isIterable(element) {
return isArray(element) || isObject(element);
}
function isArray(element) {
return element.constructor == Array;
}
function isObject(element) {
return element.constructor == Object;
}
An improved version for recursive approach suggested by #schirrmacher to print key[value] for the entire object:
var jDepthLvl = 0;
function visit(object, objectAccessor=null) {
jDepthLvl++;
if (isIterable(object)) {
if(objectAccessor === null) {
console.log("%c ⇓ ⇓ printing object $OBJECT_OR_ARRAY$ -- START ⇓ ⇓", "background:yellow");
} else
console.log("%c"+spacesDepth(jDepthLvl)+objectAccessor+"%c:","color:purple;font-weight:bold", "color:black");
forEachIn(object, function (accessor, child) {
visit(child, accessor);
});
} else {
var value = object;
console.log("%c"
+ spacesDepth(jDepthLvl)
+ objectAccessor + "[%c" + value + "%c] "
,"color:blue","color:red","color:blue");
}
if(objectAccessor === null) {
console.log("%c ⇑ ⇑ printing object $OBJECT_OR_ARRAY$ -- END ⇑ ⇑", "background:yellow");
}
jDepthLvl--;
}
function spacesDepth(jDepthLvl) {
let jSpc="";
for (let jIter=0; jIter<jDepthLvl-1; jIter++) {
jSpc+="\u0020\u0020"
}
return jSpc;
}
function forEachIn(iterable, functionRef) {
for (var accessor in iterable) {
functionRef(accessor, iterable[accessor]);
}
}
function isIterable(element) {
return isArray(element) || isObject(element);
}
function isArray(element) {
return element.constructor == Array;
}
function isObject(element) {
return element.constructor == Object;
}
visit($OBJECT_OR_ARRAY$);
var res = {
"dialog":
{
"dialog_trunk_1":{
"message": "This is just a JSON Test"
},
"dialog_trunk_2":{
"message": "and a test of the second message"
},
"dialog_trunk_3":
{
"message": "This is a test of a bit longer text. Hopefully this will at the very least create 3 lines and trigger us to go on to another box. So we can test multi-box functionality, too."
}
}
}
for (var key in res) {
if (res.hasOwnProperty(key)) {
var val = res[key];
for (var key in val) {
if (val.hasOwnProperty(key)) {
var dialog = val[key];
console.log(dialog.message);
}
}
}
}
The simpler approach is (just found on W3Schools):
let data = {.....}; // JSON Object
for(let d in data){
console.log(d); // It gives you property name
console.log(data[d]); // And this gives you its value
}
UPDATE
This approach works fine until you deal with the nested object so this approach will work.
const iterateJSON = (jsonObject, output = {}) => {
for (let d in jsonObject) {
if (typeof jsonObject[d] === "string") {
output[d] = jsonObject[d];
}
if (typeof jsonObject[d] === "object") {
output[d] = iterateJSON(jsonObject[d]);
}
}
return output;
}
And use the method like this
let output = iterateJSON(your_json_object);

How to accomplish this without using eval

Sorry for the title but I don't know how to explain it.
The function takes an URI, eg: /foo/bar/1293. The object will, in case it exists, be stored in an object looking like {foo: { bar: { 1293: 'content...' }}}. The function iterates through the directories in the URI and checks that the path isn't undefined and meanwhile builds up a string with the code that later on gets called using eval(). The string containing the code will look something like delete memory["foo"]["bar"]["1293"]
Is there any other way I can accomplish this? Maybe store the saved content in something other than
an ordinary object?
remove : function(uri) {
if(uri == '/') {
this.flush();
return true;
}
else {
var parts = trimSlashes(uri).split('/'),
memRef = memory,
found = true,
evalCode = 'delete memory';
parts.forEach(function(dir, i) {
if( memRef[dir] !== undefined ) {
memRef = memRef[dir];
evalCode += '["'+dir+'"]';
}
else {
found = false;
return false;
}
if(i == (parts.length - 1)) {
try {
eval( evalCode );
} catch(e) {
console.log(e);
found = false;
}
}
});
return found;
}
}
No need for eval here. Just drill down like you are and delete the property at the end:
parts.forEach(function(dir, i) {
if( memRef[dir] !== undefined ) {
if(i == (parts.length - 1)) {
// delete it on the last iteration
delete memRef[dir];
} else {
// drill down
memRef = memRef[dir];
}
} else {
found = false;
return false;
}
});
You just need a helper function which takes a Array and a object and does:
function delete_helper(obj, path) {
for(var i = 0, l=path.length-1; i<l; i++) {
obj = obj[path[i]];
}
delete obj[path.length-1];
}
and instead of building up a code string, append the names to a Array and then call this instead of the eval. This code assumes that the checks to whether the path exists have already been done as they would be in that usage.

More efficient Javascript

Looking for another eye on making the following Javascript more efficient.
The following JSON is produced from a Resteasy service:
var testing = {
"com:klistret:cmdb:ci:pojo:successful":true,
"com:klistret:cmdb:ci:pojo:count":1,
"com:klistret:cmdb:ci:pojo:elements":{
"com:klistret:cmdb:ci:pojo:id":123,
"com:klistret:cmdb:ci:pojo:name":"Mars",
"com:klistret:cmdb:ci:pojo:type":{
"com:klistret:cmdb:ci:pojo:id":1,
"com:klistret:cmdb:ci:pojo:name":"Environment"
},
"com:klistret:cmdb:ci:pojo:configuration":{
"#www:w3:org:2001:XMLSchemainstance:type":"Environment",
"#Watermark":"past",
"com:klistret:cmdb:ci:commons:Name":"Mars"
}
}
};
Extended the Extjs JSONReader to handle key depths higher than 2 in the createAccessor method. Wondering if there is a way to make the code more efficient? The function below will be called like function(testing, "com:klistret:cmdb:ci:pojo:configuration.#Watermark") where the com:klistret:cmdb:ci:pojo:elements property is the root.
createAccessor : function(){
var re = /[\[\.]/;
return function(expr) {
if(Ext.isEmpty(expr)){
return Ext.emptyFn;
}
if(Ext.isFunction(expr)){
return expr;
}
# THIS FUNCTION I WANT TO BE EFFICIENT
return function(obj){
while (String(expr).search(re) !== -1) {
var i = String(expr).search(re);
var key = expr.substring(0, i);
if (obj.hasOwnProperty(key)) {
obj = obj[key];
}
expr = expr.substring(i+1, expr.length);
}
return obj[expr];
};
};
}()
This is what I use. I only allow dot annotation, mind:
Ext.override(Ext.data.JsonReader, {
createAccessor: function() {
return function(expr) {
if (Ext.isEmpty(expr)) {
return Ext.emptyFn;
} else if (Ext.isFunction(expr)) {
return expr;
} else {
return function(obj) {
var parts = (expr || '').split('.'),
result = obj,
part,
match;
while (parts.length > 0 && result) {
part = parts.shift();
match = part.match(/^(.+?)(\[(\d+)\])?$/);
result = result[match[1]];
if (result && match[3]) {
result = result[match[3]];
}
}
return result;
}
}
};
}()
});
A basic optimization would be to avoid scanning the string twice with search, which is pretty slow.
The best you could do is replace all the string scanning and substring extraction with a single call to expr.split('.'), which would support accessors of the form aaa.bbb.ccc.ddd and turn them into an array like ['aaa','bbb','ccc','ddd']. The other two characters you seem to support ([ and ]) wouldn't work.
Alternately, you could do an initial match for /[^\].[]+/g over your entire string and keep the matches to obtain a similar array, but this would possibly be slower than the previous solution.

Categories

Resources