Sometimes when working with Javascript objects, it is useful to print the objects out as JSON strings and take a look at their values. Take the following simple Javascript object:
// simple Javascript object var obj = { a: { b: "foo", c: "bar" }, c: "baz", d: "qux" };
If you print it out via console.log, it looks like this:
console.log(obj); { a: { b: 'foo', c: 'bar' }, c: 'baz', d: 'qux' }
This is okay for simple objects, but as you can see, without line breaks and indenting, it gets hard to read the values of the object when it starts to get complex. It would be nice to have the object “pretty” printed in Javascript Object Notation (JSON) with proper indenting and line feeds.
Enter the JSON.stringify function:
JSON.stringify(value[, replacer[, space]]);
As it says in it's description, JSON.stringify()
converts a value to JSON notation representing it.
So, now calling console.log on a simple JSON.stringified value, the result looks like this:
console.log(JSON.stringify(obj, null, 4)); { "a": { "b": "foo", "c": "bar" }, "c": "baz", "d": "qux" }
This is great and it’s just the kind of output we want! But there’s one gotcha: sometimes Javascript objects contain circular references back to themselves. Take this object:
// simple obj with a circular reference var obj = { a: { b: "foo", c: "bar" }, c: "baz", d: "qux" }; obj.z = obj; // <-- circular reference back to itself!
Now, if you try to call the JSON.stringify function with the same variables as above, you will get a “circular structure” TypeError:
console.log(JSON.stringify(obj, null, 4)); TypeError: Converting circular structure to JSON at Object.stringify (native) at Object.<anonymous> (C:\workspace\example\example.js:50:22) at Module._compile (module.js:541:32) at Object.Module._extensions..js (module.js:550:10) at Module.load (module.js:458:32) at tryModuleLoad (module.js:417:12) at Function.Module._load (module.js:409:3) at Module.runMain (module.js:575:10) at run (node.js:348:7) at startup (node.js:140:9)
This is no good – we don’t get any information about the object; only an error message. Often, it would still be nice to see the object’s non-circular data and ignore any circular reference data.
Rob W on StackOverflow addressed this issue by calling JSON.stringify with a custom replacer function. So by tweaking his example slightly, you can create a function that ignores the circular reference data, like this:
/** * Fxn that returns a JSON stringified version of an object. * This fxn uses a custom replacer function to handle circular references * see http://stackoverflow.com/a/11616993/3043369 * param {object} object - object to stringify * returns {string} JSON stringified version of object */ function JSONStringify(object) { var cache = []; var str = JSON.stringify(object, // custom replacer fxn - gets around // "TypeError: Converting circular structure to JSON" function(key, value) { if (typeof value === 'object' && value !== null) { if (cache.indexOf(value) !== -1) { // Circular reference found, discard key return; } // Store value in our collection cache.push(value); } return value; }, 4); cache = null; // enable garbage collection return str; };
Then if you print out this function’s return value, you’ll see this:
console.log(JSONStringify(obj)); { "a": { "b": "foo", "c": "bar" }, "c": "baz", "d": "qux" }
Note that the obj.z value is not displayed in the output, but that’s the value with the circular reference. So we get exactly what we want – the object’s non-circular data and all circular references are ignored.
Here is a full example script and output:
// example.js - pretty print javascript objects as JSON /** * Fxn that returns a JSON stringified version of an object. * This fxn uses a custom replacer function to handle circular references * see http://stackoverflow.com/a/11616993/3043369 * param {object} object - object to stringify * returns {string} JSON stringified version of object */ function JSONStringify(object) { var cache = []; var str = JSON.stringify(object, // custom replacer fxn - gets around // "TypeError: Converting circular structure to JSON" function(key, value) { if (typeof value === 'object' && value !== null) { if (cache.indexOf(value) !== -1) { // Circular reference found, discard key return; } // Store value in our collection cache.push(value); } return value; }, 4); cache = null; // enable garbage collection return str; }; // simple obj with a circular reference var obj = { a: { b: "foo", c: "bar" }, c: "baz", d: "qux" }; obj.z = obj; try { // print unformatted JSON object console.log('Test 1:'); console.log(obj); console.log(); } catch (e) { console.error(e.stack + '\n'); } try { // regular print your JSON object console.log('Test 2:'); console.log(JSON.stringify(obj, null, 4)); } catch (e) { console.error(e.stack + '\n'); } try { // print your JSON object & ignore circular references console.log('Test 3:'); console.log(JSONStringify(obj)); } catch (e) { console.error(e.stack + '\n'); }
Output:
C:\workspace\example>node example.js Test 1: { a: { b: 'foo', c: 'bar' }, c: 'baz', d: 'qux', z: [Circular] } Test 2: TypeError: Converting circular structure to JSON at Object.stringify (native) at Object.<anonymous> (C:\workspace\example\example.js:52:22) at Module._compile (module.js:541:32) at Object.Module._extensions..js (module.js:550:10) at Module.load (module.js:458:32) at tryModuleLoad (module.js:417:12) at Function.Module._load (module.js:409:3) at Module.runMain (module.js:575:10) at run (node.js:348:7) at startup (node.js:140:9) Test 3: { "a": { "b": "foo", "c": "bar" }, "c": "baz", "d": "qux" }
Why aren’t there any comments? It is pretty cool. Thank you for your JSONStringify(object) function. Pretty useful at debugging. *thumbsup*
Yeah, honestly, I just wrote it up and posted it because it’s a debugging fxn that I use pretty often. I figured it might help someone else. So thanks!
John – This is awesome, I’m a newbie who doesn’t understand JS well. The solution for “circular structure” TypeError is what I needed exactly.
Thank you so much, much appreciated!