Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Limit JSON stringification depth

When stringifying an object using JSON.stringify (or something similar) is there a way to limit the stringification depth, i.e. only go n levels deep into the object tree and ignore everything that comes after that (or better: put placeholders in there, indicating something was left out)?

I know that JSON.stringify takes a replacer function of the form function (key, value) but I didn't find a way to get the depth in the original object of the current key-value-pair handed to the replacer function.

Is there a way to do this with the default JSON.stringify implementation? Or have I reached a point where I should just implement the stringification myself? Or is there another stringification library you can recommend that has this option?

like image 344
Joachim Kurz Avatar asked May 09 '13 16:05

Joachim Kurz


People also ask

Is there a limit to JSON Stringify?

So as it stands, a single node process can keep no more than 1.9 GB of JavaScript code, objects, strings, etc combined. That means the maximum length of a string is under 1.9 GB. You can get around this by using Buffer s, which store data outside of the V8 heap (but still in your process's heap).

What does JSON stringify() method do?

JSON.stringify() The JSON.stringify() method converts a JavaScript object or value to a JSON string, optionally replacing values if a replacer function is specified or optionally including only the specified properties if a replacer array is specified.

Why to stringify JSON?

The JSON. stringify() method in Javascript is used to create a JSON string out of it. While developing an application using JavaScript, many times it is needed to serialize the data to strings for storing the data into a database or for sending the data to an API or web server.

What is replacer in JSON stringify?

let json = JSON.stringify(value [, replacer, space]) The value is the value to convert to a JSON string. The replacer is either a function that alters the behavior of the stringification process or an array which servers as a filter for the properties of the value object to be included in the JSON string.


2 Answers

I wanted to stringify an object on the first level without including a third-party library / too much code.

In case you look for the same, here is a quick one-liner to do so:

var json = JSON.stringify(obj, function (k, v) { return k && v && typeof v !== "number" ? (Array.isArray(v) ? "[object Array]" : "" + v) : v; }); 

The top level object will have no key, so it's always simply returned, however anything that is not a "number" on the next level will be casted to a string incl. a special case for arrays, otherwise those would be exposed further more.

If you don't like this special array case, please use my old solution, that I improved, too:

var json = JSON.stringify(obj, function (k, v) { return k && v && typeof v !== "number" ? "" + v : v; }); // will expose arrays as strings. 

Passing an array instead of an object in the top level will work nonetheless in both solutions.

EXAMPLES:

var obj = {    keyA: "test",    keyB: undefined,    keyC: 42,    keyD: [12, "test123", undefined]  }  obj.keyD.push(obj);  obj.keyE = obj;    var arr = [12, "test123", undefined];  arr.push(arr);    var f = function (k, v) { return k && v && typeof v !== "number" ? (Array.isArray(v) ? "[object Array]" : "" + v) : v; };  var f2 = function (k, v) { return k && v && typeof v !== "number" ? "" + v : v; };    console.log("object:", JSON.stringify(obj, f));  console.log("array:", JSON.stringify(arr, f));  console.log("");  console.log("with array string cast, so the array gets exposed:");  console.log("object:", JSON.stringify(obj, f2));  console.log("array:", JSON.stringify(arr, f2));
like image 166
Martin Braun Avatar answered Sep 21 '22 13:09

Martin Braun


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:

  1. _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).
  2. 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.

like image 25
James Wilkins Avatar answered Sep 21 '22 13:09

James Wilkins