Here is a function that respects the built-in JSON.stringify()
rules while also limiting depth:
function stringify(val, depth, replacer, space) {
depth = isNaN(+depth) ? 1 : depth;
function _build(key, val, depth, o, a) { // (JSON.stringify() has it's own rules, which we respect here by using it for property iteration)
return !val || typeof val != 'object' ? val : (a=Array.isArray(val), JSON.stringify(val, function(k,v){ if (a || depth > 0) { if (replacer) v=replacer(k,v); if (!k) return (a=Array.isArray(v),val=v); !o && (o=a?[]:{}); o[k] = _build(k, v, a?depth:depth-1); } }), o||(a?[]:{}));
}
return JSON.stringify(_build('', val, depth), null, space);
}
How it works:
_build()
is called recursively to build the nested objects and arrays to the requested depth. JSON.stringify()
is used to iterate over each object's immediate properties to respect the built-in rules. 'undefined' is always returned from the internal replacer so no JSON is actually constructed yet. Keep in mind, the first time the internal replacer is called the key is empty (which is the item to be stringified).
JSON.stringify()
is called on the final result to produce the actual JSON.
Example:
var value={a:[12,2,{y:3,z:{q:1}}],s:'!',o:{x:1,o2:{y:1}}};
console.log(stringify(value, 0, null, 2));
console.log(stringify(value, 1, null, 2));
console.log(stringify(value, 2, null, 2));
{}
{
"a": [
12,
2,
{}
],
"s": "!",
"o": {}
}
{
"a": [
12,
2,
{
"y": 3,
"z": {}
}
],
"s": "!",
"o": {
"x": 1,
"o2": {}
}
}
(for a version that handles cyclical references, see here: https://stackoverflow.com/a/57193345/1236397 - includes a TypeScript version)
Update: Fixed a bug where empty arrays rendered as empty objects.