Flex Serialization

There’s a few good use-cases for serialization; for me the first and foremost is the performance combined with ease of use.
In AIR we have SQLite access, or in a bad case we can write some format to a file and recover it later somehow via parsing. But why? That’s a lot of work… And it’s not the fastest approach either.

And what about if we’re doing a browser application that we want to save a state of something, but don’t want to host the storage and write a web service to handle data?
We don’t have stealth access to the filesystem directly, which makes it hard (errr, impossible) to silently store things on behalf of the user.

We can actually tackle both easily using serialization.

Quick note: I could quite possibly be wrong about some of these concepts, as they’re just things I’ve come across, and can’t find an exorbitant amount of documentation about these topics.

Overview

First thing’s first… Let’s take a quick look at some of the interfaces/classes we might run into:

The act of Serializing data in AS3

A quick serialization is rather simple, and we use the ByteArray to convert objects for us into AMF bytes. This is done in-memory, so we want to make sure our client systems can handle the amount/quantity of data we’re serializing. For most situations, this probably is not much of a concern.

Setting up a serialized Class

So in order for the Flash Player to know what to de/serialize to/from, we have to assign an Alias to our class.
This is done simply by using the RemoteObject metadata tag to define the mapping.

package com.killerspaz.examples.serialize.vo
{
    [RemoteClass(alias="com.killerspaz.examples.serialize.vo.JunkVO")]

    public class JunkVO
    {
        //.... Class Implementation ....//
    }
}

Notice that I made my alias the fully qualified class name (that means I include the package name) my alias.
This is NOT required, but I personally suggest doing this. Keep reading to find out why…

If you want to map to a different name (such as something easier to remember, or to match the package space of a remote system) you can easily do so, and is acceptable.
The caveat to this is that in order to deserialize your objects, you MUST register the class alias using registerClassAlias():

package com.killerspaz.examples.serialize.vo
{
    [RemoteClass(alias="MyJunkVO")]

    public class JunkVO
    {
        //.... Class Implementation ....//
    }
}

//** Before deserializing anywhere, register the class alias:
registerClassAlias('MyJunkVO", JunkVO);

It’s typically best to register your aliases as soon as possible, such as on application startup.

Simple Serialization of an Object

Here’s a quick snippet of how to serialize your object:

var myJunkVo:JunkVO = new JunkVO();
var bytes:ByteArray = new ByteArray();
// ...
// set some properties on myJunkVo
// ...

bytes.writeObject(myJunkVo);     //Write our object to the ByteArray
bytes.position = 0;              //Reset position of ByteArray to the beginning

As a quick note, this is one half of the steps required to clone most objects at runtime. I’ll probably go over this in another post as well – in the meantime, check out mx.ObjectUtil::copy().

Done! Your object is now a ByteArray – you’ve serialized! Ok we can all go home now… Wait… I’m not actually done.

The whole object, and nothing but the whole object

a.k.a. It’s not working right

If you found yourself going through this post, or any other post you’ve found, but things just aren’t working right – this section is most likely the concept you missed.

There’s 2 considerations to serializing your data:

  1. If your object hierarchy is made of completely public properties, then there really is nothing extra special you need to do in order to serialize…
    Seriously, I’ve never had to do anything fancy for fully public classes.
  2. If your object has complex objects as children, which utilizes encapsulation and needs to save its complete state; well then we have some extra footwork to do.
    This is necessary for objects that we can’t easily derive states from our public variables, and have maybe collections of custom objects. Essentially, the nature of the data is valuable for an instance to function properly.
    It’s an all or nothing thing too – you either rely on all public vars and everything’s dandy, or if you want to serialize private/protected vars you’ll need to ALSO write the public ones!

So #1 is what we saw above. Assuming all members of JunkVO are public, we can safely serialize as demo’d.

As it turns out, #2 isn’t that difficult to pull off, but can be rather tedious depending on your model.

Implementing External Serialization

If you implement the IExternalizable interface, when a ByteArray::writeObject()/readObject() is called, internally it checks for these interfaces and calls the appropriate method.

The trick to implementing these methods is that you do it in the exact same order. For every write, you read, no exceptions! You’re essentially manipulating the ByteArray your object is stored in.

public function writeExternal(output:IDataOutput):void
{
    output.writeObject(this._createdDate);
    output.writeInt(this.somePropInt);
    output.writeObject(this.somePropString);
}

public function readExternal(input:IDataInput):void
{
    this._createdDate      = input.readObject();
    this.somePropInt       = input.readInt();
    this.somePropString    = input.readObject();
}

See, pretty simple!
Just remember:

  • Sub-objects could also need to implement the interface for recursive de/serialization to occur properly. If they appear as null or default values when inspecting, count on this being the issue!
  • Every property MUST be written and read in the same exact order.

Things to do with your serialized ByteArray

Well, you can take your ByteArray, and shove it right up your ……. storage mechanism :D

The easiest way to handle this is writing the ByteArray directly into a file:

protected var _fileReference:FileReference;

protected function save():void
{
    var ba:ByteArray = new ByteArray();
    ba.writeObject(someObjectInstance);
    ba.compress(); //compress to save space
    this._fileReference = new FileReference();
    //Prompt the user to save out to a file. Extension is made up here btw
    this._fileReference.save(ba, "OutputFile.amf");
}

But wait, there’s more!

While this works, and is quick and useful – I prefer to encode the binary data into a String.
This may not be that big of a deal for most of you, but for me personally I like to be able to copy the output and paste it into a sandbox app to see what’s going on.
I might make this app as simple and quick as possible, so I only have a text field to put the data, and binary data gets all wacky when copy/pasting.
I might also want to store the data into a database column to void a bunch of key/value tables that muddy things up. Binary data can be a pain working with in a database, and generally adds to confusion.

So for that, I’ve implemented a nice utility class I call SerializeUtils. You can see it in full effect in the example, or just take a look at the interface – it’s super simple!

Using the same file-save technique above, you can now save this text out to a file – just replace the byteArray reference with your string!

Troubleshooting

Things don’t always go as planned. Here’s some things that I’ve found along the way:

  • When deserializing, and things are turning into Objects, or ObjectProxy, then we need to check our Alias’.
  • If deserializing a built-in Flash object (e.g.: Date), be sure to register the class alias with the fully qualified class name.
    • Not all objects will serialize correctly. Unfortunately the only work around is to store the variables in a custom class of your own, and serialize that data.
  • An error about converting an Object when writing a ByteArray: Ensure your metadata alias, and/or your registerClassAlias() is correct/exists.
  • For writing to a file, make sure the client is using Flash Player 10+, this is a new-ish feature.

Example, Source, and Additional Info

To prove this works, check out the example. Once you serialize the object, copy the Base64Encoded string, and close/reopen the SWF. If you paste it back in and deserialize, you should see that the values are the same as you entered them originally, including the date the object was created!

Some Additional Information:

About these ads

About killerspaz

I'm a developer that loves to tinker with cutting edge technology. I have recently been playing with the Flash Platform (AS3/Flex), Android (custom roms, replacement apps, scripting), and looking at opportunities in the mobile markets.

Posted on 02.02.2011, in Flash, Flex and tagged , , , , , , , , . Bookmark the permalink. 5 Comments.

  1. This is not far short of brilliant. Thank you.

  2. great post, I’ll be using this to save data from my up and coming code gen tool :)

  3. Yeah bro, I just need to get the serialization working so users can reload as their model changes and I bring out new features. I’m even writing the code to write the code for the code gen tool (because I need to do stuff with strings from normal as3 code etc)

    I keep finding things I can automate in my daily dev

    I plan to gen code for php, html and CFM and asp perhaps from the same data model

  4. Very informative post. I wanted to implement a save feature for my game and this has helped a lot.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 301 other followers

%d bloggers like this: