26

So, JSON.stringify provides a great way to turn a JS object like:

var baz = {"foo":1, "bar":someFunction};

in to a JSON string like:

{"foo":1}

It does this with an optional second argument that controls which fields should be serialized:

JSON.stringify(baz, ["foo"]);

That's great, but there's a problem. Let's say your "baz" is actually the property of another object, and you want to serialize that other object:

someObject.baz = {"foo":1, "bar":someFunction};
JSON.stringify(someObject, ["baz"]);

Well, normally you would just define a toJSON method on baz, eg.:

someObject.baz = {"foo":1, "bar":someFunction};
someObject.baz.toJSON = function() { /* logic to "toJSON" baz*/ }
JSON.stringify(someObject, ["baz"]);

Now, as I mentioned earlier, we have the perfect logic to "toJSON" baz already:

someObject.baz.toJSON = function() {
    return JSON.stringify(baz, ["foo"]);
}

but if you try putting that in to your toJSON, you'll get a recursion error, because stringify will trigger the toJSON, which will trigger the stringify, which will ... :-(

You can work around this with a hack:

someObject.baz.toJSON = function() {
    var oldToJON = this.toJSON;
    this.toJSON = null;
    var ret = JSON.stringify(baz, ["foo"]);
    this.toJSON = oldToJON;
    return ret;
}

But ... that just seems wrong. So, my question is: is there any way you can utilize the nifty built-in serialization power of JSON.stringify inside a toJSON method of an object (without having to hide the toJSON method itself during the stringify operation)?

1 Answer 1

47

Crockford's json2.js says:

A toJSON method does not serialize: it returns the value represented by the name/value pair that should be serialized, or undefined if nothing should be serialized.

So you are simply expected to return the value that you want serialized. In your case, baz.toJSON should simply return the portion of the baz object that you want serialized:

someObject.baz.toJSON = function() {
  return { foo: this.foo };
};
6
  • So then if I only want to serialize certain fields, I have to do: Commented Nov 27, 2010 at 2:11
  • someObject.baz.toJSON = function() { var ret = {}; for (var property in this) { if (!property in listOfPropertiesToSerialize) continue; ret[property] = this[property; } return ret; }; Commented Nov 27, 2010 at 2:12
  • There's no way to just provide the list of fields and let the the built-in JSON stuff (whether it's Crockford's or the browser-supplied ones) filter things out, the way the stringify method does? Commented Nov 27, 2010 at 2:13
  • @machineghost: You could do that, but look out for chained properties and make sure to use this.hasOwnProperty(property). If your list of properties is static, you could just use object notation to directly build a new object. Edit: No, I don't believe that is possible. toJSON works differently from stringify. Commented Nov 27, 2010 at 2:14
  • 1
    @machineghost: Skip the filter and add a toJSON method to someObject as well, that only returns the baz property. Commented Nov 27, 2010 at 2:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.