Serializing display objects

Ha! I serialized a display object. Didn’t think it could be done, but with a little work it can be. See my previous post about overriding Transform and Matrix objects for a little more context. I’m jumping right in to how I did it.

So the main problem before was the Transform class requiring a parameter in its constructor. So I subclassed Transform and made that parameter optional. Turns out if you pass in null to the superclass you get an error too, so if no displayObject is passed in I use a Shape. (I figured it was one of the cheaper display objects to make, though between that and Bitmap I’m not sure.) No I need to subclass Sprite so that it uses my new Transform subclass.


public class MyTransform extends Transform
	public function MyTransform(displayObject:DisplayObject = null)
		super(displayObject || new Shape());


public class MySprite extends Sprite
	private var _transform:MyTransform;
	public function MySprite()
		_transform = new MyTransform(this);
	public override function get transform():Transform
		return _transform;
	public override function set transform(value:Transform):void
		_transform = new MyTransform(this);
		_transform.colorTransform = value.colorTransform;
		_transform.matrix = value.matrix;

Proof that it works:

// register all the classes required to store a sprite
registerClassAlias("MySprite", MySprite);
registerClassAlias("MyTransform", MyTransform);
registerClassAlias("MySoundTransform", SoundTransform);
registerClassAlias("MyMatrix", MyMatrix);
registerClassAlias("MyColorTransform", ColorTransform);

var sprite:MySprite = new MySprite();
trace(sprite.transform is MyTransform); // true

var ba:ByteArray = new ByteArray();
ba.position = 0;
sprite = ba.readObject();

trace(sprite.transform is MyTransform); // true

Yeah!! It totally worked! And I could add that sprite to the display list and everything!

Now a couple of things to note: when serializing only read/write variables are stored. So Sprite’s and Shape’s graphic property is not stored. Neither is parent or any of the children because they aren’t referenced by any properties that can be written too. So if you draw on your shape or sprite with the drawing API it won’t be stored. Of course, this can be worked with if you have methods that draw on creation, or if you store some of Flash 10’s new IGraphicData objects publicly which can be drawn. You could also store all your sprite’s children in a public array that has to be set like the filter array for it to alter the display list. Then you could serialize your whole display tree. :) Might be fun.

Oh, and one more quickie that I double tested to make sure. I was told at one of our uFlash meetings that private properties are stored in serialized bytearrays. This didn’t seem consistent with what I understood, so I tested and retested and it is only public properties with getters and setters that are stored. Tyler said people sometimes had issues when they put [Bindable] at the top of a class and the complier made everything public (even privates) so that they would work with binding. But that’s a compiler issue with how it rewrites code. Private properties are not stored in bytearrays.

10 Responses to “Serializing display objects”

  1. person Says:

    Is this technique exclusive to Flash Player 10 or to Flex? I’m trying this in Flash CS3/FP 9 and coming up empty. byteArray.readObject() returns null.

  2. Jacob Wright Says:

    Make sure you are getting all the registerClassAlias’s and that you are at the beginning of your bytearray (ba.position = 0)

    If you’re following the code I have above and its still not working let me know, but it should work in Flash 9, flex or flash CS3.

  3. Alan Says:

    Hi .. i have problems with serializing ..
    i’ve wrote my class extending sprite and overrided Transform class .. but when i load a serialized instance of my class , i still get an argument count mismatch error .. do i have to override all geom classes?

    all the best

  4. Jacob Wright Says:

    Alan: Be sure to register all the aliases as stated in the example. And an extra tidbit: I found you can even register two classes to the same alias and they’ll both write out with that alias but when they come back out of the bytearray they are set to the last registered class. So you can serialize Sprites and have them come back out as MySprites.


    registerClassAlias(“Sprite”, Sprite);
    registerClassAlias(“Sprite”, MySprite);
    registerClassAlias(“Transform”, Transform);
    registerClassAlias(“Transform”, MyTransform);

    And of course, don’t forget to register the other stuff too.

  5. Alan Says:

    hi .. first of all big thanks .. i’ve managed to serialize sprites .. now i’m standing before much larger problem .. i’m trying to serialize a sprite containing bitmap ..

    i have overrided bitmapdata class so it can be serialized (the same way as you did with transform class) .. i wanted to try making something like this –
    when creating my bitmapdata object i want to save base64 encoded bitmap in a public property (inside my bitmapdata instance) as string and then, when cloned bring it back ..

    so i was thinking .. or better .. asking if you know if there is a trick that will (internaly) automaticly (when cloned) check for that string and bring my bitmap back


  6. Jacob Wright Says:

    Another way to handle serialization is using the IExternalizable interface. If you implement it, the two methods you use to implement it allow you to control exactly what is written when serialized. I did some bitmap serialization with something like the following (note, this hasn’t been tested, it is an example of something like what I did):

    public class MyBitmap extends Bitmap implements IExternalizable
    	public function writeExternal(output:IDataOutput):void
    		output.writeObject(transform.matrix); // takes care of x, y, rotation, etc.
    		var byteArray:ByteArray = bitmapData.getPixels(new Rectangle(0, 0, bitmapData.width, bitmapData.height));
    	public function readExternal(input:IDataInput):void
    		transform.matrix = input.readObject();
    		var width:int = input.readInt();
    		var height:int = input.readInt();
    		bitmapData = new BitmapData(width, height, true, 0);
    		var byteArray:ByteArray = new ByteArray();
    		bitmapData.setPixels(new Rectangle(0, 0, width, height), byteArray);
  7. Alan Says:

    Hi .. again big thanks for Your help.
    Writing raw pixel data reulted in EoF error, so i compresed that bytearray and converted it to Base64 string. When reading i just converted it back and uncompressed

    it looks like this

    import flash.display.*;
    import flash.utils.ByteArray;
    import flash.utils.IExternalizable;
    import flash.utils.IDataInput;
    import flash.utils.IDataOutput;
    import flash.geom.Rectangle;
    import com.dynamicflash.util.Base64;

    public class LayerBitmap extends Bitmap implements IExternalizable {

    public function LayerBitmap() {
    // ..

    public function writeExternal(output:IDataOutput):void {
    var bytes:ByteArray = bitmapData.getPixels(new Rectangle(0,0,bitmapData.width,bitmapData.height));

    public function readExternal(input:IDataInput):void {
    this.bitmapData=new BitmapData(input.readInt(),input.readInt(),true,0);
    var bytes:ByteArray = Base64.decodeToByteArray(input.readUTF());
    bitmapData.setPixels(new Rectangle(0, 0, bitmapData.width, bitmapData.height), bytes);

    thnaks again

  8. Jacob Wright Says:

    When working with ByteArrays I’ve always gotten the EoF error because I didn’t specify how much to read. In readExternal you could try input.readBytes(byteArray, byteArray.bytesAvailable); or something similar. I’m sorry I don’t have time to test it out right now but I personally would try to go without base64ing it as that makes it bigger. But if it works and you’re ok with the size, great!

  9. Alan Says:

    Yes the size with b64 is bigger .. but the bytearray gets compressed, so it’s not so big.


  10. Nimesh Nanda Says:

    These are very nice example but i need a source code above example…..