Skip to main content
Participant
January 5, 2013
Question

Custom toJSON() method is not called for each property

  • January 5, 2013
  • 1 reply
  • 854 views

I'm getting familiar with JSON in ActionScript 3 (FP 11), and experimenting with custom toJSON methods. The Developer's Guide section on Native JSON Support clearly states:

JSON.stringify() calls toJSON(), if it exists, for each public property that it encounters during its traversal of an object. A property consists of a key-value pair. When stringify() calls toJSON(), it passes in the key, k, of the property that it is currently examining. A typical toJSON() implementation evaluates each property name and returns the desired encoding of its value.

However, both the subsequent examples on the very same page and my own tests indicate that, on the contrary, toJSON() is only called once for the whole object, and no argument is passed for k. Take the following trivial class:

public class JSONTest {
    public var firstProperty:int = 1;
    public var secondProperty:String = "Hello world";
    public function toJSON(k:String):* {
        trace("Calling toJSON on key", k);
        return this.toString();
    }
}

According to the documentation this should cause the following to print:

Calling toJSON on key firstProperty

Calling toJSON on key secondProperty

And the output should basically be the same as without a custom toJSON method:

{ "firstProperty" : 1, "secondProperty" : "Hello world" }

Instead, this just throws an error because nothing at all is passed as k.

So, is the documentation wrong and the engine is not supposed to call toJSON for every property, or is the engine incorrectly only calling it once?

This topic has been closed for replies.

1 reply

sinious
Legend
January 5, 2013

Your toJSON method should provide all the necessary logic to convert your entire class into a proper JSON string.

For your test class, e.g.:

public function toJSON(k:String):*

{

          return "{\"firstProperty\":' + firstProperty + ', \"secondProperty":\" + secondProperty + "\"}";

}

The reason you'd check k is if you ever anticipate using JSON.stringify() in that same class but not on the current class, so you'd want to just do a simple test like:

if (k is JSONTest)

{

     // code above here

}

else

{

     // return the object sent so it's re-sent to the built in optimized parser

     return k;

}

Participant
January 6, 2013

That sounds intriguing, but doesn't work for me. The k passed to the toJSON function is an empty string, not the object itself, even when k is not type constrained in the method definition:

public function toJSON(k:*):* {
    if (k == null)
        trace("k == null");
    else
        trace("k:" + getQualifiedClassName(k) + " = " + k.toString());
    return k;
}

Calling trace("Stringify result =", JSON.stringify(new JSONTest())) prints the following:

k:String = 
Stringify result = ""

Do you have some functioning code that demonstrates stringify using toJSON in a manner resembling the documentation?

Participant
January 6, 2013

I just figured out what the documentation means. k refers to the name of a property to which the entire instance being stringified is assigned, not to the names of the properties of the instance being stringified. An example is much clearer:

var wrapper:Object = { prop1 : 1, prop2 : new JSONTest() };
trace("Stringify result =", JSON.stringify(wrapper));

Prints:

k:String = prop2
Stringify result = {"prop2":{"firstProperty":1,"secondProperty":"Hello world"},"prop1":1}

So k is the name of the property on wrapper, not on the JSONTest instance. When stringify is called directly on an instance of JSONTest, k is empty because the JSONTest instance is not assigned as a property of another object, it is just all by itself. Either way the value returned by toJSON must be a string or object representing the stringified form of the entire instance.