What You Might Not Know About JSON.stringify()

On January 29, 2013, in JavaScript, by Jim Cowart

Nearly any developer spending even a moderate amount of time in JavaScript has had to, at some point, utilize JSON.stringify (and it’s counterpart, JSON.parse). JSON – JavaScript Object Notation – has become the go-to data-interchange format for many developers – with multiple languages capable of serializing to JSON, not just JavaScript itself. If you’re up late some night, unable to sleep, check out the history of JSON (tl;dr – Douglas Crockford is the brain behind it).

When writing in JavaScript, JSON.stringify is the way we take a value and serilalize it to a string value representing the object:

JSON.stringify({
    name: "Jim Cowart",
    country: "Jimbabwe"
});
// produces: "{"name":"Jim Cowart","country":"Jimbabwe"}"

JSON.stringify("Oh look, a string!");
// produces ""Oh look, a string!""

JSON.stringify([1,2,3,4,"open","the","door"]);
// produces "[1,2,3,4,"open","the","door"]"

I won’t belabor all of the rules of what gets serialized, you can read more about that here.  But it’s essential you know the following:

  • a value of undefined, a function or XML value are ommitted – except when….
  • If you have an array with undefined, a function or an XML value, it will be emitted as a null value

Let’s see about that:

JSON.stringify({
    doStuff: function() { },
    doThings: [ function() {}, undefined ]
});
// produces: "{"doThings":[null,null]}"

“Great, Jim. I get it. It’s a data interchange format. Behavior isn’t serialized. Most of the libraries I use these days handle serializing for me under the hood. So why write a blog post on JSON.stringify?! SHEESH!”

I know, right? Still – there are some nice tricks that can come in handy. Many of the larger applications I’ve worked on recently have had debug flags that can be flipped to enable various console logging from different components active on the page. While we obviously don’t want to sift through endless lines of console.log information all the time – when it becomes necessary, wouldn’t it be nice if it were a bit more readable?

// what if this:
'{"name":"Jim Cowart","location":{"city":{"name":"Chattanooga","population":167674},"state":{"name":"Tennessee","abbreviation":"TN","population":6403000}},"company":"appendTo"}'

// could be formatted like this, automagically?
"{
    "name": "Jim Cowart",
    "location": {
        "city": {
            "name": "Chattanooga",
            "population": 167674
        },
        "state": {
            "name": "Tennessee",
            "abbreviation": "TN",
            "population": 6403000
        }
    },
    "company": "appendTo"
}"

JSON.stringify actually takes 3 parameters (JSON.stringify(value [, replacer [, space]])), the 3rd of which – the “space” argument – allows you to specify either a string character to use for indentation, or a number. If you pass a number, that many spaces (up to 10) will be used for indentation:

var person = {
    name: "Jim Cowart",
    location: {
        city: {
            name: "Chattanooga",
            population: 167674
        },
        state: {
            name: "Tennessee",
            abbreviation: "TN",
            population: 6403000
        }
    },
    company: "appendTo"
};
// If you think the world only looks right through 2-
// spaced-indentation, then you'll appreciate this:
JSON.stringify(person, null, 2);
/* produces:
"{
  "name": "Jim Cowart",
  "location": {
    "city": {
      "name": "Chattanooga",
      "population": 167674
    },
    "state": {
      "name": "Tennessee",
      "abbreviation": "TN",
      "population": 6403000
    }
  },
  "company": "appendTo"
}"
*/
// If you're with "Team Tabs™", then you can
// kiss those space indentations goodbye by
// passing \t as the last arg
JSON.stringify(person, null, "\t");
/* produces:
"{
    "name": "Jim Cowart",
    "location": {
        "city": {
            "name": "Chattanooga",
            "population": 167674
        },
        "state": {
            "name": "Tennessee",
            "abbreviation": "TN",
            "population": 6403000
        }
    },
    "company": "appendTo"
}"
*/

So – what about the second argument, simply passed as “null” in the above example? That’s the “replacer” argument. It can be an array or a function. If you pass an array, it should contain the keys you want exported from the object:

// assuming the person object is the object from above
JSON.stringify(person, ["name", "company"], 4);
/* produces:
"{
    "name": "Jim Cowart",
    "company": "appendTo"
}"
*/

If you pass a function, it takes two arguments: the key and the value:

//  a bit contrived, but it shows what's possible
// FYI - the entire value being serialized is the first thing
// passed to the replacerFn, followed by each key on the
// object, hence the check to see if key is falsy
var replacerFn = function(key, value) {
    if(!key || key === 'name' || key === 'company') {
        return value;
    }
    return; // returning undefined omits the key from being serialized
}
JSON.stringify(person, replacerFn, 4);
/* produces:
"{
    "name": "Jim Cowart",
    "company": "appendTo"
}"
*/

You can use the replacer function approach to blacklist member names (in contrast to how the replacer argument as an array whitelists member names). I’ve found this approach to be useful when I need quick and targeted logging of certain values from a DOM element, but I want to prevent a serialization exception due to attempting to serialize a circular reference (which would occur if I drank some antifreeze and attempted to do something ridiculous like JSON.stringify(document.body)). Feel free to browse this fiddle for a simple example of using a replacer function to blacklist based on member name(s).

Of course, another option for customizing an object’s JSON serialization is to add a “toJSON” method to the object:

var person = {
    name: "Jim Cowart",
    location: {
        city: {
            name: "Chattanooga",
            population: 167674
        },
        state: {
            name: "Tennessee",
            abbreviation: "TN",
            population: 6403000
        }
    },
    company: "appendTo",
    toJSON: function () {
        return {
            booyah : this.name + ' , employer: ' + this.company
        };
    }
};

// The value returned from toJSON above is what
// will actually get stringified:
JSON.stringify(person, null, 4);
/* produces:
"{
    "booyah": "Jim Cowart , employer: appendTo"
}"
*/

“Seriously, Jim, there are bigger fish to fry than to write pretty JSON to a console.” You don’t have to convince me, I’m with you 110%. But I will say this, nicely formatted JSON makes a serious difference when you are wire-tapping a message bus for debugging visibility into the messages being published, or logging websocket payloads. Being able to selectively serialize objects by whitelisting member names with a replacer array argument (or blacklisting with a replacer function) can be very handy on DOM events/elements – helping you track down issues, or simply give you better visibility into complex interactions, without de-railing you with serialization exceptions.

Some further reading/exploring:
You’ll notice I never EVER said “JSON object” when describing JSON, or objects. Ben Alman has a great post explaining why “There’s no Such Thing as a JSON Object“. The only thing he fails to mention is that saying “JSON Object” kills kittens and puppies. By the dozens. True Story.

If you feel the need to write your own ‘circular reference safe’ JSON serializer in JavaScript, check out @getify’s idea here.

Also – kudos to Elijah Manor and/or Doug Neiner. I’m fairly certain one of them was the first to mention to me that JSON.stringify can format output.

So, go forth. Stringify.

Tagged with:  
  • Daniel Thul

    Thanks! Realy useful!

  • JeremyRDeYoung

    love the fact you used the word automagically – great blog by the way!

  • http://kun.io/ William P. Riley-Land

    Nice overview!

  • Priamo74

    Very nice, man. Hope I’ll never forget about this lesson. Great job.

  • http://twitter.com/WhateverCode Whatever Code

    Great, nice tricks man!

  • Pingback: Things about JSON.stringify() I didn’t know « MikeScott8 Programming Thoughts

  • Pingback: Friday Links #240 | Blue Onion Software *

  • http://www.wpguru.com.au/ Wordpress Developer Sydney

    Hi Jim,
    Great tutorial. Really impressive.
    Are you on Google+.

    • http://ifandelse.com Jim Cowart

      Thanks! Yes, I am – though I admit I’m not on it as much as twitter: me at ifandelse dot com.

  • Ethan Zhang

    good to learn, thanks very much

  • Pingback: Rounded Corners 394 — Shake to shoot | Labnotes

  • Pingback: Web links 05/02/2013 — Nevma Developers Blog

  • http://blakesimpson.co.uk/ Blake Simpson

    Thanks for the post, I haven’t ever seen stringify using multiple arguments before.

  • David Swift

    Thanks, this helped me correct a bit of nastiness I caused by implementing domains in my node.js project.

  • MR NoJon

    Is it possible to build a json string i a loop? Someting like this?
    Im an early bird on json an pls xcuse my stupid question.

    var jsonData = “[";
    for (var i = 0; i < 1; i++) {
    var datefrom = new Date();
    var dateto = new Date();
    var name = "name" + i;

    jsonData += "{"name" : "" + i + "","desc" : "" + i + "","values:" : [{"id":"" + i + "","from":"/Date(1320301600000)/","to":"/Date(1320301600000)/","desc":"" + i + "","label":"" + name + "","customClass":"ganttRed"}]}"
    }
    jsonData += "]";

    • http://kun.io/ William P. Riley-Land

      Yes, it’s totally possible. But, there’s just about no reason to do that in production code. Also, this may be a matter of personal preference, but I consider it easier to read strings with a bunch of quotation marks when written like: ‘{“name”:”‘ + name + ‘”, “desc”:”‘ + desc + ‘”}” or “It’s a beautiful day” :)

      • MR NoJson

        But im still struggling with this:

        var jsonData = “[";
        for (var i = 0; i < 5; i++) {
        var datefrom = new Date();
        var dateto = new Date();
        var name = "name" + i;
        jsonData += '{"name":"' + i + '","desc" : "' + i + '","values:" : [{id":"' + i + '","from":"/Date(1320301600000)/","to:"/Date(1320301600000)/","desc":"' + i + '","label":"' + name + ",customClass:ganttRed}]}"
        }
        jsonData += "]";

        im trying to dynamicly build a json for this Project:

        http://taitems.github.io/jQuery.Gantt/

        But im so lost right now…

        • http://kun.io/ William P. Riley-Land

          var a = [];

          a.push({ name: ‘x1′ });

          a.push({ name: ‘x2′ });

          console.log(JSON.stringify(a));

  • http://twitter.com/yodirkx Rudie Dirkx

    Actually “Douglas Crockford is [not!] the brain behind it”. He’s just the one that ‘standardized’ it. He admittedly “took it from brighter minds” (parafrasing). Details.

  • Pingback: JSON.stringify()’s arguments | Bram.us

  • Robert Chesley

    Very helpful and thorough post. Many Thanks.

  • Design by Adrian

    Thanks for this! I found the 2nd parameter to be… magical!

  • Wagner

    Crockford is full of crock. Who names a function “stringify”, it;s not even a word! That’s one way to ‘messify’ a language. He should follow convention and have named it toString.

    • Axel

      No, JSON.toString(); would just convert the JSON object to a string…

  • bigFan

    Thanks for the great explanation!
    I was getting a cyclic error doing a set item to local storage on something saved from a previous session. But then I passed in the replacer array and space arguments and it all works perfectly now. Thanks!

  • srikanth

    Thank you so much you saved my time, you made my day