Why Objects Serialized With ByteArray Are So Small
We know that you can automatically serialize anything to a ByteArray
and that it’s faster and smaller than XML or JSON, but why is it so much smaller? Today’s article investigates a bit and reveals the secret that makes it such an efficient format and how that can save you a lot of manual work when it comes time to deserialize the ByteArray
.
To help investigate, let’s work with a simple Person
class:
class Person { var first:String; var last:String; var mother:Person; var father:Person; }
When serializing to ByteArray
with its writeObject
function, one of the great features is that it’ll recursively serialize your custom objects and won’t double-encode anything since it supports references/pointers internally. So you can simply do this:
var mother:Person = new Person(); mother.first = "Susan"; mother.last = "Smith"; var father:Person = new Person(); father.first = "Bruce"; mother.last = "Smith"; var child:Person = new Person(); child.first = "Jimmy"; child.last = "Smith"; child.mother = mother; child.father = father; bytes.writeObject(new <Person>[child,father,mother]);
There’s no need to worry that the child Person
object will store a duplicate copy of the mother and father Person
objects. At least that’s what the AMF specification that defines the ByteArray.writeObject/readObject
file format says.
How can we test this to observe it in action? Well, let’s make a little test where we give a Person
a really long first and last name and then serialize that Person
object a bunch of times to the ByteArray
:
var reallyLongString:String = ""; for (var i:int = 0; i < 1024; ++i) { reallyLongString += i; } var person:Person = new Person(); person.first = reallyLongString; person.last = reallyLongString; person.mother = person; var bytes:ByteArray = new ByteArray(); for (var i:int = 0; i < 1000000; ++i) { bytes.writeObject(person); } trace(bytes.length);
If there weren’t support for pointers/references in AMF, this ByteArray
would be full of duplicates of reallyLongString
and the ByteArray
would be huge. Instead, we simply get 3 million bytes. Considering that there are 1 million Person
objects and they all have two huge strings, it’s safe to say that AMF is doing a good job at safeguarding against duplicate objects.
As one final test though, let’s see just how far AMF takes this pointer/reference concept. Does ByteArray.readObject
preserve them in the AS3 objects it reads back? Here’s one final test that reads back the first two Person
objects and checks if each of their mother
fields point to the same Person
:
bytes.position = 0; var obj1:Object = bytes.readObject(); var obj2:Object = bytes.readObject(); trace(obj1.mother == obj2.mother);
This will actually print “true”, meaning that the references are preserved all the way through the serialization and deserialization process. This is a really handy feature that makes the AMF format of ByteArray.readObject
and ByteArray.writeObject
(especially in combination with registerClassAlias
) really great to use in comparison to the more common XML and JSON. It’s faster, smaller, and preserves types and references. AMF is definitely worth considering the next time you need to serialize something.
Spot a bug? Have a suggestion or a question? Post a comment!
#1 by ben w on May 13th, 2013 ·
Nice article, this is a good showcase for why it is a suitable format for 3D scenes pointers reduce repeated geometry and allow for easy linking of models and materials and textures etc…
#2 by Michal Wroblewski (@wrobel221) on May 13th, 2013 ·
Random question related to objects stored in a ByteArray. I have a huge xml configuration for DragonBones and it takes a LOT of time to parse and on mobile it’s a killer for performance. Can I save parsed object to a ByteArray and have it distributed and loaded thru different platforms or I will encounter some problems when decoding ByteArray? Not sure if byte order can be different or something else.
Great article BTW.
#3 by jackson on May 13th, 2013 ·
AMF is portable between the various platforms that Flash supports (desktop browser, iOS, Android, etc.) and is even available on some other platforms, such as Java with Adobe’s BlazeDS and PHP with AMFPHP. You shouldn’t have problems with byte order, but you probably want to make sure you use a consistent
ByteArray.endian
setting.#4 by Michal Wroblewski (@wrobel221) on May 14th, 2013 ·
By byte orded I meant endian. So when calling writeObject it will be encoded in AMF, right? Before that I’ll set endian and I’m ready to go. I suppose I should use
registerClassAlias
with that. Much thanks Jackson#5 by MikeA on May 13th, 2013 ·
Have you tried playing with the IExternalizable interface? That allows you to define how you want an object to be read from and written to a byte stream. I’ve only used it for objects that store atomic data (ints, Numbers, Strings), but it’d be interesting to see if it does the same object referencing that writeObject does for generic Objects.
#6 by jackson on May 14th, 2013 ·
I’ve used it, but haven’t yet written any articles about it. I’ll make a note to do a followup article.