Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JSON.stringify whitelisting with nested objects

Given following example:

var test = {
    "company_name": "Foobar",
    "example": "HelloWorld",
    "address": {
        "street": "My Street 12",
        "example": "BarFoo",
        "details": "Berlin",
    }
}

console.log(JSON.stringify(test, ['company_name','address','street','example']));

// What I actually want
// console.log(JSON.stringify(test, ['company_name','address.street','address.example']));

How can I use JSON's stringify function to deal with nested objects properly?

Since I have huge JSON objects it happens that a key of a nested object is identical to it's "parent" object. I would like to specify my whitelist more granulary.

like image 330
Christopher Will Avatar asked Jul 06 '13 20:07

Christopher Will


People also ask

Does JSON Stringify work on nested objects?

If your deeply-nested object or array only includes certain primitive values (strings, numbers, boolean, and null), then you can use JSON. parse() & JSON. stringify() to deep copy without issues.

Does JSON support nested values?

Attributes and event data can contain nested (JSON) values—arrays, objects, and arrays of objects.

How do I add a nested object to JSON?

To create a nested object, call createNestedObject() . To create a nested array, call createNestedArray() .

Does JSON Stringify call toJSON?

JSON.stringify() calls toJSON with one parameter, the key , which has the same semantic as the key parameter of the replacer function: if this object is a property value, the property name. if it is in an array, the index in the array, as a string.


2 Answers

If you're willing to go to the effort of whitelisting, then you can establish an array of valid keys, which can provide the ability to nest similar to how many systems do JSON nesting (a . separator, or any separator of your choosing).

var whitelistedObj = whitelistJson(obj, ["company_name", "example", "address.street", "address.example"]);

function whitelistJson(obj, whitelist, separator) {
    var object = {};

    for (var i = 0, length = whitelist.length; i < length; ++i) {
        var k = 0,
            names = whitelist[i].split(separator || '.'),
            value = obj,
            name,
            count = names.length - 1,
            ref = object,
            exists = true;

        // fill in any empty objects from first name to end without
        //  picking up neighboring fields
        while (k < count) { // walks to n - 1
            name = names[k++];
            value = value[name];

            if (typeof value !== 'undefined') {
                if (typeof object[name] === 'undefined') {
                    ref[name] = {};
                }

                ref = ref[name];
            }
            else {
                exists = false;
                break;
            }
        }

        if (exists) {
            ref[names[count]] = value[names[count]];
        }
    }

    return object;
}

I have a JSFiddle showing its usage as well (to ensure it actually worked on my admittedly small sample set).

like image 58
pickypg Avatar answered Oct 22 '22 21:10

pickypg


You can add toJSON method in your huge JSON objects:

var test = {
    "company_name": "Foobar",
    "example": "HelloWorld",
    "address": {
        "street": "My Street 12",
        "example": "BarFoo",
        "details": "Berlin",
    },
    toJSON: function () {
        return {
            company_name: this.company_name,
            address: {
                street: this.address.street,
                example: this.address.example
            }
        }
    }
}

And, you get:

console.log(JSON.stringify(test)); // "{"company_name":"Foobar","address":{"street":"My Street 12","example":"BarFoo"}}"

Or, you can use some filter function: (this function using lodash)

function filter(object, keys, sep) {
    sep = sep || '.';
    var result = {};
    _.each(keys, function (key) {
        var keyParts = key.split(sep),
            res = object, 
            branch = {},
            branchPart = branch;

        for (var i = 0; i < keyParts.length; i++) {
            key = keyParts[i];
            if (!_.has(res, key)) {
                return;
            }

            branchPart[key] = _.isObject(res[key]) ? {} : res[key];

            branchPart = branchPart[key];
            res = res[key];
        }

        _.merge(result, branch);
    });
    return result;    
}
console.log(JSON.stringify(filter(test, ['company_name', 'address.street', 'address.example']))); // "{"company_name":"Foobar","address":{"street":"My Street 12","example":"BarFoo"}}"

Check out jsfiddle http://jsfiddle.net/SaKhG/

like image 28
Alex Fitiskin Avatar answered Oct 22 '22 22:10

Alex Fitiskin