Custom Serialization using IExternalizable

I started a blog to document solutions to questions that I found myself frequently answering on the Flex forums. So the first topic up for discussion is using custom serialization with value objects in RemoteObject. This solution takes advantage of the ActionScript 3 API flash.utils.IExternalizable which is compatible with Java’s java.io.IExternalizable API. By implementing these interfaces on the client and server your value objects can take control of their serialization and customize what data is sent over the wire.

A common use case for using externalizable classes is to include read-only properties in serialization. While there are other approaches to achieve this for server code, there aren’t many approaches available for client code. The server uses the Java Beans API when serializing classes so one could change their value objects to implement java.beans.BeanInfo to customize introspection, however there isn’t an equivalent for this in ActionScript. This may be fine if data is not editable on the client – but many applications involve data manipulation so read-only information needs to be transmitted to maintain the idenity and/or state of an instance. A simpler, brute-force approach might be to track when a setter has been called and throw an error if it is called again – but the fact that your value object had read-only properties wouldn’t be obvious from your API.

So, for an elegant solution that works for both the client and server we are left with the option of making our classes externalizable for two-way custom serialization. Thankfully this is relatively straight-forward. The client ActionScript class simply implements flash.utils.IExternalizable. This API requires two methods readExternal() and writeExternal() which take flash.utils.IDataInput and flash.utils.IDataOutput streams respectively. The implementations of these methods mirror the server Java class, which implements java.io.Externalizable – also with two methods readExternal() and writeExternal() taking java.io.ObjectInput and java.io.ObjectOutput streams respectively.

While the IDataInput and IDataOutput classes let you design your own protocol and write fundamental data types such as byte, int, and UTF-8 encoded Strings, most implementations will take advantage of the readObject() and writeObject() methods respectively as these use AMF 3 to efficiently deserialize and serialize ActionScript objects. (Remember that AMF 3 allows: (a) objects to be sent by reference to avoid redundant instances from being serialized, to retain object relationships and to handle cyclical references, (b) object traits to be sent so that the description of a type is only sent once rather than them repeated for each instance, and (c) reoccuring strings can be sent by reference to again avoid redundant information from being sent). One may even decide to omit property names altogether in their externalizable classes’ custom serialization code and rely on a fixed order to send just the property values.

To see a code example, check out externalizable.zip. While this example focuses on serializing read-only properties there may be many other usages for custom serialization such as omitting properties, avoiding redundant serialization of information or including properties from custom namespaces.


Notice how the server Java writeExternal() method:public void writeExternal(ObjectOutput out) throws IOException{    out.writeObject(id);    out.writeObject(name);    out.writeObject(description);    out.writeInt(price);}…mirrors the client readExternal method in ActionScript:public function readExternal(input:IDataInput):void{    _id = input.readObject() as String;    name = input.readObject() as String;    description = input.readObject() as String;    price = input.readInt();}A similar relationship exists for the reverse situation for sending instances back from the client to the server.

9 Responses to Custom Serialization using IExternalizable

  1. karthik says:

    Pete,I am getting errors on flex side during deserialization .ArgumentError: Error #2004: One of the parameters is invalid.at flash.utils::ObjectInput/readObject()I guess that flex does not recognize/link flash.utils.IExternalizable interface and is trying to deserialize all the classes in a normal way.Is this a bug or is there a work aroundThanksKarthik

  2. Pete says:

    It’s not that Flash or Flex does not recognize the interface, it’s that it can’t find a class definition that matches the reported implementation. You need to create a reference to the Class somewhere in your Flex application (i.e. type a private static variable with that class, or use that Class in a casting statement on the result or a method signature – whatever – just some way to make the compiler note the dependency on this so it will link it into the SWF).

  3. Troy Gardner says:

    Great post Pete, I’ve extended what you’ve done with some alternate approaches to dealing with data you don’t want serialized and evolving classes.http://troyworks.com/blog/?cat=30

  4. Den says:

    I discovered that by default FDS don’t contain RemoteClass metdata in section of flex-config.xml. That eat about of my 2 hours. Should I post bug somewhere?

  5. Marcio says:

    I create the following function———————————package com.cerebrum.utils {import mx.rpc.events.ResultEventimport mx.controls.Alert;public class Result {[Embed(“/assets/images/dialog-information.png”)]public var IconDialogInformation:Class;public function Result(event:ResultEvent,MyObject:Object):void {Alert.show(“teste”, “teste”, 4, null, null, IconDialogInformation);}}}———————————I as followsimport Result;Result(event, meuobjeto);———————————Is presented in the following error flex builder1137: Incorrect number of arguments. Expected no more than 1.———————————How should I inform the function that actually has 2 parameters?

  6. Pete Farland says:

    You cannot register an event listener that has more than one argument. If you want to retain more information, get the mx.rpc.AsyncToken instance which is returned immediately in ActionScript from a call to any RemoteObject method, decorate this token (it’s dynamic, so it should be just like a “hashmap” like AS Object is) with extra info, and then get the token back from the ResultEvent instance in your handler.

  7. Troy A. Binford says:

    Hey Pete,I have an object that has a property “children” (an ArrayCollection) that contains more of this same object, and that child object could have more children as well. The object implements IExternalizable. If my test object has no children, it will externalize just fine. Well that’s a problem because I want to externalize the children as well. My writeExternal is below:public function writeExternal(output:IDataOutput):void {output.writeInt(this.type);this.children.writeExternal(output);output.writeUTF(this.word1);output.writeUTF(this.word2);output.writeUTF(this.word3);}With children in the object’s AC it crashes on line 452 of ArrayList. It’s the writeObject(_source) call. Is it because the source contains my custom object? How do I make it work?

  8. Troy A. Binford says:

    Ignore previous comment please. It was the same as the first issue. As soon as I registered the class the serialization worked. Thanks!

  9. Jeffrey A Lage says:

    i don’t think that i’m double posting what karthik posted, but…so if you forget to add the [RemoteClass(alias=”com.blah.Foo”)] metadata, FR/LCDS will have problem sending an object to the CLIENT and typing it correctly. It also wont have any trouble sending an object to the SERVER, so long as you didn’t implement IExternalizable. If you did, and you leave of the metadata, it just doesn’t call the writeExternal() method and tries to serialize the entire object graph. i no clue what’s wrong with my object where if it tries to serialize the entire object it get the same error as karthik, but once i put in the missing metadata, it called my writeExternal() method and all was well.