Working with Native JSON in Flash Player 11

July 21st, 2011 by Todd Anderson

With the recent public release of the next candidates for the Flash Player and AIR runtimes, I wanted to check out one of the features that I had been waiting to see as part of the player for some time. No, not Molehill. Native JSON support! Boring, right? Maybe i’ll address Stage3D (née Molehill) in another article, but initially I am actually more curious to check out the native JSON support.

If you are unfamiliar with JSON, it is lightweight, interchangeable format that is human-readable and has a familiar structure that allows for ease in parsing and generating data. You can read more about it at http://www.json.org.

History… sorta

For applications targeting the Flash Platform, there was a time when you would typically go grab the as3corelib library and use the JSON class to encode or decode JSON data – actually, the time is still now, or until these runtimes are out of candidacy. Though the JSON class from the as3corelib library works well and was a welcome addition to the community, it sits outside of the player globals and is implemented in ActionScript. That’s not to say it is necessarily slow, but – because of its implementation – it is inherently slower than having native support of JSON within the player.

Future… kinda

With the advent of native JSON support in the Flash Player 11 and AIR 3 runtimes, we now are afforded serialization and deserialization at a faster speed as well as an API that matches that of the JavaScript implementation.

JSON API

You can head over to the online docs and check out the JSON API for a deeper explanation, but the JSON class basically has two static methods:

If coming from working with the JSON class from as3corelib, these will most likely look different than the methods you are used to. That’s alright, they do relatively the same thing and, depending on the level of control you want, it may just be a matter of the methods being named differently. I do want to go over what has been made available in these methods in moving toward the use of the native JSON class, however, as they can be quite interesting and possibly useful depending on the design of your application.

JSON.stringify()

The JSON.stringify() method does essentially what you may be familiar with as JSON.encode() from the as3corelib – put object in, get string out. However, the global JSON class of FP11 now has a couple more parameters that can be used to your advantage when turning that object into a JSON string.

The replacer parameter can be supplied as a list of String or Number that correspond to the key-value pairs which you want to serialize to a JSON string. It can also be supplied as a Function delegate that will be invoked upon each key-value pair that allows you to handle the data directly in either modifying the value being generated or – in the case of returning undefined – removing it from serialization.

Here is a quick example of how you can assign a replacer delegate and remove a value from being serialized to the generated JSON string:

That gives you a little peek at the potential that a replacer has when generating JSON, but you can imagine that it could be valuable for, say, hashing passwords and such on the client side before being sent over the wire.

The other parameter in JSON.stringify() – space – can be a String or Number and is used in prettifying the generated JSON string by providing whitespace and readability. Using the previous example, if we provided the value of 4 as the argument for spacer, the trace output would no longer be on one line and look more like this:

Pretty!

JSON.parse()

Let’s take a look at the parse method. Again, if coming from as3corelib JSON, it will probably just be a change of calling:

[FP 11 global: JSON]

instead of:

[as3corelib: JSON]

However, there is a reviver parameter on the parse() method which can be of use if you wish to handle working with the key-value pairs during the parse operation of the value object. The signature of the Function supplied as the reviver should have two parameters defined – key and value – and should return the modified value in order to be stored in the object.

Here is a quick example of using the parse() method with a reviver that turns each string value to upper case:

One thing to note here is that if you return undefined from the reviver delegate, you can actually remove the property and its value from the generated object, which is useful but also is something to look out for if things seem to be going wrong. Another thing to note here is that the last key-value pair passed through this reviver delegate is the actual object itself – keep that in mind when using a reviver and make sure to check that you are not modifying the whole object you are parsing.

Going a step further

Now this new API is well and good and we could be on our way sending and receiving JSON data and punch our time card and grab a beer, but there may come a time where you actually want to have more of a workflow in streamlining the generation and parsing of this data using real model objects in your project rather than generic Objects.

Now it is true that as long as the property values held on a custom object are those that are supported in the JSON format – string, number, boolean, array and null – that you could supply the custom object to the JSON.stringify() method and it will properly generate the JSON object for you. Going the other way – parsing – however, is not going to inherently create or write to that custom object that you may have previously serialized; it will just generate a generic object. Of course there is nothing stopping you from traversing the properties on that object and setting them on a new custom instance, but this is an opportunity in which we might want to think about how we can go about creating a design in which we can easily map back to custom objects. Things I like to think about and bore people with. Don’t fall asleep on me.

To get an idea of what i am talking about, say we have a Person class which serves as a model for a person in the context of our application. It has your basic properties on it which are standard value types – like name, company, etc from previous examples. We can certainly generate a JSON string and pass that around like so:

Now, in our perfect world, that would trace [object Person], wherein the JSON parser was knowledgable to map the object to a Person. There is that reviver parameter on JSON.parse() but that won’t help us much in mapping back to a custom instance as it only handles the key-value pairs and not the object as a whole.

One solution is to serialize the classname down with the generated JSON object, so that when it is received on the client, we can inflate a custom instance based on the associated class. So, if you can imagine, the models you will use in your application all extend a base model that handles setting this classname that is accessible and writable to a JSON object, eg:

In this base model class, there is a model accessor which is the qualified classname of the subclass. It also exposes a description accessor which allows easy access to the list of read-write properties on the subclass. It is marked as transient to denote that it should not be considered enumerable when generating the associated JSON object.

Now that we have our base model, any subclasses will be serialized with a model property representing the qualified clas name which can be used to inflate the custom object upon parse. Modifying the Person class used in a previous example, the class now looks like this:

We just have to remember to call super() in the constructor or ensure that the _clazz property value is that of the subclass. From here, all that is left is using that model property to instantiate a new instance and fill the available property values from the parsed JSON object received. Here is a quick working example:

In the parseToModel() method in this example, we see how the read-only model property is passed in the flash.utils.getDefinitionByName() function to inflate a new instance of the associated model – Person – that was previously serialized to a JSON object.

One thing to remember is that a reference to the class in which you want to use to inflate a new instance of must be compiled into the SWF, otherwise you will get Runtime Errors. There are a couple ways in which you can ensure that the reference is included, and I will leave that up to you to explore.

View and download the example

That is just one solution in implementing the ability to map JSON objects back to custom instances, but I am sure there are many more and I would like to hear your thoughts on them in the comments.

Conclusion

Native JSON support to the runtimes is a much welcomed addition and I am very happy to see it finally become a reality, and it is good to see – in my opinion – an API that matches that of the one on the JavaScript side. Hopefully I covered some of what can be accomplished with it and how it can benefit you in any current or future projects. Go grab the beta players, and the playerglobal SWC and have some fun!

, , ,