Scenario
After reading this answer I realized that I could create object starting from a JSON literal.
So I guessed that I could do the opposite just using this useful JSON method:
JSON.stringify(myObject)
.
So I did as follow:
function MyObject(id, value, desc)
{
this.id = id;
this.value = value;
this.desc = desc;
this.toJSON = function()
{
return JSON.stringify(this);
}
}
But when I run this stuff (demo) a Maximum call stack size exceeded
error occurs.
After googling a bit, I found two references that explain this behaviour:
If I get right, .toJSON
overrides the .stringify
. So if the first one calls the second one a loop is generated.
Questions
toJSON
is a kind of reserved of special keyword?.toJSON
name into .display
. Not so elegant. Is there another solution?Think it's because toJSON
is semi reserved: stringify
will check the object and see if it's has a method called toJSON
and then try to call it to string the result.
A workaround can be: (Not sure about the reliablity of this code)
var obj = {
value: 1,
name: "John",
toJSON: function() {
var ret,
fn = this.toJSON;
delete this.toJSON;
ret = JSON.stringify(this);
this.toJSON = fn;
return ret;
}
}
Usage:
obj.toJSON(); // "{\"value\":1,\"name\":\"John\"}"
obj.lastName = "Smith";
obj.toJSON(); // "{\"value\":1,\"name\":\"John\",\"lastName\":\"Smith\"}"
Maybe using a clousure is a little prettier: (And then I think I can say it's safe)
var obj = {
value: 1,
name: "John",
toJSON: (function() {
function fn() {
var ret;
delete this.toJSON;
ret = JSON.stringify(this);
this.toJSON = fn;
return ret;
}
return fn;
})()
}
So after reading @filmor's comment i thoght about another way to handle this. Not that pretty but it works.
Using Function.caller I can detect if fn
is called using JSON.stringify
var obj = {
value: 1,
name: "John",
toJSON: (function() {
return function fn() {
var ret;
delete this.toJSON;
ret = JSON.stringify(this);
if ( fn.caller === JSON.stringify ) {
ret = JSON.parse( ret );
}
this.toJSON = fn;
return ret;
}
})()
}
Question 1, is toJSON
reserved?
I'm not sure if it reserved, but for example the native Date object uses toJSON to create a stringified date representation:
(new Date()).toJSON(); // -> "2012-10-20T01:58:21.427Z"
JSON.stringify({d: new Date()}); // -> {"d":"2012-10-20T01:58:21.427Z"}"
Question 2, an easy solution:
create your custom stringify function that ignores toJSON methods (you may add it to the already existing global JSON
):
JSON.customStringify = function (obj) {
var fn = obj.toJSON;
obj.toJSON = undefined;
var json = JSON.stringify(obj);
obj.toJSON = fn;
return json;
}
now it's very easy to use in all your objects:
function MyObject(id, value, desc)
{
this.id = id;
this.value = value;
this.desc = desc;
this.toJSON = function()
{
return JSON.customStringify(this);
}
}
To make it even more easy additionally add:
JSON.customStringifyMethod = function () {
return JSON.customStringify(this);
}
Now your objects might look like:
function MyObject(id, value, desc)
{
this.id = id;
this.value = value;
this.desc = desc;
this.toJSON = JSON.customStringifyMethod;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With