Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Strange behaviour in JSON.stringify with replacer function

Consider this:

var object = {date: new Date()};

JSON.stringify(object, function(key, value) {
    console.log('---');
    console.log('key:', key);
    console.log('value:', value);
    console.log('value instanceof Date:', value instanceof Date);

    return value;
});

As documentation says:

The replacer parameter can be either a function or an array. As a function, it takes two parameters, the key and the value being stringified. The object in which the key was found is provided as the replacer's this parameter. Initially it gets called with an empty key representing the object being stringified, and it then gets called for each property on the object or array being stringified.

...

If you return any other object, the object is recursively stringified into the JSON string, calling the replacer function on each property, unless the object is a function, in which case nothing is added to the JSON string.

But if you run the code, you will get this:

---
key: 
value: { date: Fri Jan 10 2014 02:25:00 GMT+0100 (CET) }
value instanceof Date: false
---
key: date
value: 2014-01-10T01:25:00.262Z
value instanceof Date: false

It means, that the date property was stringified before replacer function has been called. Is it a normal behaviour or am I missing something? How can I affect format of Date stringification without overriding it's default toJSON method?

Thanks!

Edit

Based on responses and next research, documentation seems to be unclear at this point and toJSON is beeing called before the replacer function. Based on Pills's response, this snippet should do the job:

var object = {date: new Date };

JSON.stringify(object, function(key, value) {
    if (typeof(value) === 'object') {
        for (var k in value) {
            if (value[k] instanceof Date) {
                value[k] = value[k].getTime();
            }
        }
    }
    return value;
});

Edit #2

Xotic750's solution is much better than previous one.

like image 881
Patrik Šimek Avatar asked Jan 10 '14 01:01

Patrik Šimek


People also ask

What does JSON Stringify do with functions?

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.

Can JSON Stringify throw an error?

Errors and Edge Cases JSON. stringify() throws an error when it detects a cyclical object. In other words, if an object obj has a property whose value is obj , JSON. stringify() will throw an error.

What is the purpose of JSON Stringify () and parse () methods?

The JSON. parse() function is used to convert a string into a JavaScript object while the JSON. stringify() function is used to convert a JavaScript object into a string.

Why JSON Stringify does not work on array?

The JSON array data type cannot have named keys on an array. When you pass a JavaScript array to JSON. stringify the named properties will be ignored. If you want named properties, use an Object, not an Array.


2 Answers

Its quiet old but just to complete it.

According to this Q/A and the MDN JSON.stringify article, the replacer is called with instance of the object in which the key was found so no need to change prototype or do another tricks:

function log(what) {
   what = what || "";
   document.getElementById("out").innerText += what + "\n";
}

function replacer(key, value) {
   console.log(this);
   log("Key: '" + key + "' = '" + value + "'");
   log("this = " + this);
   log("this[key] = " + this[key]);
   log("this[key] instanceof Date = " + (this[key] instanceof Date));
   log("this instanceof Date = " + (this[key] instanceof Date));
   
   if (this[key] instanceof Date) {
      return "This was a date: " + this[key].getTime();
   }
   
   return value;
}

var obj = {
   d: new Date()
};

var result;
result = JSON.stringify(new Date(), replacer);
log();
log(result);
log();
result = JSON.stringify(obj, replacer);
log();
log(result);
<pre id="out"></pre>
like image 156
Fis Avatar answered Oct 26 '22 23:10

Fis


Perhaps you could do your formatting before passing the object to JSON, but otherwise here is a possibility:

JSON uses an objects toJSON method before calling the replacer.

So, before calling stringify store the toJSON method and then restore it afterwards.

var object = {
        date: new Date()
    },
    storeToJSON = Date.prototype.toJSON;

delete Date.prototype.toJSON;

JSON.stringify(object, function (key, value) {
    console.log('---');
    console.log('key:', key);
    console.log('value:', value);
    console.log('value instanceof Date:', value instanceof Date);

    return value;
});

Date.prototype.toJSON = storeToJSON;

On jsFiddle

like image 35
Xotic750 Avatar answered Oct 27 '22 01:10

Xotic750