Exit
  • Global community
    • Language:
      • Deutsch
      • English
      • Español
      • Français
      • Português
  • 日本語コミュニティ
  • 한국 커뮤니티
0

Custom toJSON() method is not called for each property

New Here ,
Jan 05, 2013 Jan 05, 2013

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?

TOPICS
ActionScript
793
Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Jan 05, 2013 Jan 05, 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;

}

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
New Here ,
Jan 05, 2013 Jan 05, 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?

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
New Here ,
Jan 05, 2013 Jan 05, 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.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Jan 05, 2013 Jan 05, 2013
LATEST

Again, the toJSON method assumes it belongs to the class you nest it in and expects YOU to provide the implementation to JSON serialize the class (which IS an object (instance)). Theres no reason to pass a reference of itself, to itself, so 'k' is empty or it would be an infinite loop.

Like I also mentioned, the branch code checking if k==JSONTest is safety in case you expect to nest multiple objects to pass into the toJSON override. If one object is a reference to its own class it fires off your serialization implementation. Otherwise it returns it as an object which triggers the built in JSON stringify.

Thats a common implementation because classes often reference other classes (reference objects) and you don't need to worry about how to parse them.

The only reason you return an empty string is you didn't provide a JSON string of your class.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines