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

  1. (general) Why this design choice? toJSON is a kind of reserved of special keyword?
  2. (specific) I solved the stackoverflow bug changing the .toJSON name into .display. Not so elegant. Is there another solution?
share|improve this question
2  
+1 for using the stackoverflow keyword. No, kidding, it's a good question. – gion_13 Oct 9 '12 at 8:52
@ADC Remember to accept your open questions. – NULL Oct 12 '12 at 11:45
Why do you use a custom toJSON method if there is nothing special? – Bergi Oct 19 '12 at 13:22
feedback

3 Answers

up vote 1 down vote accepted
+50

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;
}
share|improve this answer
It works! Thanks a lot. – ADC Oct 23 '12 at 7:38
feedback

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;
        }
    })()
}
share|improve this answer
1  
Found that out at roughly the same time as you (after crashing Firefox during the course ;)). One comment: If you do it like this you break normal JSON.stringify(obj), since it will interpret the object as a string and escape and add surrounding " chars. – filmor Oct 9 '12 at 9:03
@filmor I ran into the same problem. But think I found a solution using fn.caller – NULL Oct 9 '12 at 10:20
Interesting, but quite a lot of stuff just to use toJSON intead of display :). Note that there is a non standard remark in the Function.caller page. Anyway, an elegant solution. – ADC Oct 9 '12 at 13:17
feedback
function MyObject(value)
 {
   this.value = value;
   this.toJSON = function()
   {
     return "{ value: '" + value + "' }";
   }
 }
share|improve this answer
No, the aim is to 'JSONfy' all the object. I improved the example snippet to clarify. – ADC Oct 9 '12 at 8:36
1  
Thanks @ADC , I learned something new. I can now create object that will be serialized customly – karaxuna Oct 9 '12 at 8:47
feedback

Your Answer

 
or
required, but never shown
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.