The ByteArray class, introduced in Flash Player 9, has a pair of very powerful functions alongside all the usual ones like writeInt. It actually allows you to read and write AS3 objects and provides an easy, fast, and compact way to serialize anything without writing any code. Today’s article explores shows you how to harness the power of these functions to improve your data serialization and deserialization.

First, let’s start off with a class we’d like to serialize/deserialize:

class Person
{
	public var first:String;
	public var last:String;
	public var age:int;
}

This is a simple class as it only contains public variables with built-in types. Still, let’s serialize it:

var person:Person = new Person();
person.first = "William";
person.last = "Shatner";
person.age = 80;
 
var bytes:ByteArray = new ByteArray();
bytes.writeObject(person);

See how easy that was? The deserialization is just as easy:

bytes.position = 0; // back to where we wrote the object
var obj:Object = bytes.readObject();
trace(obj.first + " " + obj.last + " is " + obj.age); // William Shatner is 80

The downside here is that the object we get back is not typed: we get a plain Object. To get around this, use flash.net.registerClassAlias to give it a name:

registerClassAlias("Person", Person); // Alias the Person class to "Person"
 
var person:Person = new Person();
person.first = "William";
person.last = "Shatner";
person.age = 80;
 
var bytes:ByteArray = new ByteArray();
bytes.writeObject(person);

Now when we read it back, we’ll get a typed object:

bytes.position = 0; // back to where we wrote the object
var ws:Person = bytes.readObject() as Person;
trace(ws.first + " " + ws.last + " is " + ws.age); // William Shatner is 80

This is all very simple and very powerful. There’s no need to write a bunch of AS3 code to serialize and deserialize to your own binary format or XML or JSON (unless you’re looking for an interchange format) when you can just use readObject and writeObject.

Now let’s throw in a private variable and see what happens:

class Person
{
	public var first:String;
	public var last:String;
	private var __secret:String;
	public var age:int;
 
	public function setSecret(secret:String): void { __secret = secret; }
	public function getSecret(): String { return __secret; }
}
 
registerClassAlias("Person", Person);
 
var person:Person = new Person();
person.first = "William";
person.last = "Shatner";
person.age = 80;
person.setSecret("I'm a Canadian");
 
var bytes:ByteArray = new ByteArray();
bytes.writeObject(person);
 
var ws:Person = bytes.readObject() as Person;
trace(ws.first + "'s secret is: " + ws.getSecret());

Unfortunately, this last line won’t work and you’ll get:

William's secret is: null

The reason for this is that private variables are not serialized… usually. You can get around this limitation by providing a getter and setter:

class Person
{
	public var first:String;
	public var last:String;
	private var __secret:String;
	public var age:int;
 
	public function set secret(secret:String): void { __secret = secret; }
	public function get secret(): String { return __secret; }
}

Now we’ll get what we expected:

William's secret is: I'm a Canadian

You can easily write out collections of objects, too:

var person:Person = new Person();
person.first = "William";
person.last = "Shatner";
person.age = 80;
person.secret = "I'm a Canadian";
 
var person2:Person = new Person();
person2.first = "George";
person2.last = "Takei";
person2.age = 74;
person2.secret = "I can speak Spanish";
 
// Serialize just like any other object
bytes.writeObject(new <Person>[person, person2]);
 
// Deserialize just like any other object
var people:Vector.<Person> = bytes.readObject() as Vector.<Person>;
 
for each (var p:Person in people)
{
	trace(p.first + " " + p.last + " is " + p.age + ". Secret: " + p.secret);
}

And you get:

William Shatner is 80. Secret: I'm a Canadian
George Takei is 74. Secret: I can speak Spanish

These functions even support inheritance:

class Entity
{
	private static var __nextID:int = 100;
	public var id:int = __nextID++;
}
class Person extends Entity
{
	public var first:String;
	public var last:String;
	private var __secret:String;
	public var age:int;
 
	public function set secret(secret:String): void { __secret = secret; }
	public function get secret(): String { return __secret; }
}
 
var person:Person = new Person();
person.first = "William";
person.last = "Shatner";
person.age = 80;
person.setSecret("I'm a Canadian");
 
var person2:Person = new Person();
person2.first = "George";
person2.last = "Takei";
person2.age = 74;
person2.setSecret("I can speak Spanish");
 
// Serialize just like any other object
bytes.writeObject(new <Person>[person, person2]);
 
// Deserialize just like any other object
var people:Vector.<Person> = bytes.readObject() as Vector.<Person>;
 
for each (var p:Person in people)
{
	trace(p.first + " " + p.last + "(ID: " + p.id + ") is " + p.age + ". Secret: " + p.secret);
}

With no change to how we serialize or deserialize, we get what we expect:

William Shatner (ID: 100) is 80. Secret: I'm a Canadian
George Takei (ID: 101) is 74. Secret: I can speak Spanish

What we have here is a simple way to serialize any AS3 class to a ByteArray and deserialize it back into an object. But how fast is it compared to, say, XML? Let’s look at a quick performance test:

package
{
	import flash.display.*;
	import flash.utils.*;
	import flash.text.*;
	import flash.net.*;
 
	public class SerializeAnything extends Sprite
	{
		private var __logger:TextField = new TextField();
		private function log(msg:*): void { __logger.appendText(msg + "\n"); }
		private function row(...cols): void
		{
			__logger.appendText(cols.join(",")+"\n");
		}
 
		public function SerializeAnything()
		{
			stage.align = StageAlign.TOP_LEFT;
			stage.scaleMode = StageScaleMode.NO_SCALE;
 
			var logger:TextField = __logger;
			logger.autoSize = TextFieldAutoSize.LEFT;
			addChild(logger);
 
			var beforeTime:int;
			var afterTime:int;
			var i:int;
			var SIZE:int = 100000;
 
			registerClassAlias("Person", Person);
 
			var people:Vector.<Person> = new Vector.<Person>(SIZE);
			for (i = 0; i < SIZE; ++i)
			{
				var person:Person = new Person();
				person.secret = "I'm a Canadian";
				person.first = "William";
				person.last = "Shatner";
				person.age = 80;
 
				people[i] = person;
			}
 
			row("Operation", "Time");
 
			beforeTime = getTimer();
			var ba:ByteArray = new ByteArray();
			ba.writeObject(people);
			afterTime = getTimer();
			row("ByteArray serialize", (afterTime-beforeTime));
 
			beforeTime = getTimer();
			var xmlString:String = "<people>";
			for (i = 0; i < SIZE; ++i)
			{
				person = people[i];
				xmlString += "<person first=\"" + person.first
						+ "\" last=\"" + person.last
						+ "\" age=\"" + person.age
						+ "\" secret=\"" + person.secret + "\" />";
			}
			xmlString += "</people>";
			var xml:XML = new XML(xmlString);
			afterTime = getTimer();
			row("XML serialize", (afterTime-beforeTime));
 
			beforeTime = getTimer();
			ba.position = 0;
			people = ba.readObject() as Vector.<Person>;
			afterTime = getTimer();
			row("ByteArray deserialize", (afterTime-beforeTime));
 
			beforeTime = getTimer();
			people = new Vector.<Person>();
			for each (var personNode:XML in xml.person)
			{
				person = new Person();
				person.first = personNode.@first;
				person.last = personNode.@last;
				person.age = personNode.@age;
				person.secret = personNode.@secret;
				people.push(person);
			}
			afterTime = getTimer();
			row("XML deserialize", (afterTime-beforeTime));
 
			row();
 
			row("Format", "Size");
			row("ByteArray", ba.length);
			row("XML", xmlString.length);
		}
	}
}
class Person
{
	public var first:String;
	public var last:String;
	private var __secret:String;
	public var age:int;
 
	public function set secret(secret:String): void { __secret = secret; }
	public function get secret(): String { return __secret; }
}

I ran this performance test with the following environment:

  • Flex SDK (MXMLC) 4.5.1.21328, compiling in release mode (no debugging or verbose stack traces)
  • Release version of Flash Player 11.1.102.55
  • 2.4 Ghz Intel Core i5
  • Mac OS X 10.7.2

And got these results:

Operation Time
ByteArray serialize 113
XML serialize 554
ByteArray deserialize 115
XML deserialize 403

Performance Chart

Clearly, ByteArray outperforms XML in both serialization (5x faster) and deserialization (4x faster). So, how does it do when comparing the storage size required? Well, the above performance test shows those figures too:

Format Size
ByteArray 1000063
XML 7400017

Size Chart

Here too ByteArray is the obvious winner over XML with a 7x smaller data size.

In conclusion, ByteArray‘s readObject and writeObject methods provide AS3 programmers with a great way to serialize their objects for sending over a network or storing for later retrieval. They support strong typing of these objects, collections like Vector, class inheritance, and don’t require us to write any of the serialization or deserialization code.

Care to share your thoughts on serialization and deserialization? Post a comment!