
Recently O'Reilly sponsored the Adobe AIR Cookbook Cook-Off contest, a chance for developers to submit recipes and win prizes. And many of the top entries can be found in O'Reilly's latest, Adobe AIR 1.5 Cookbook, including the winning entry from Greg Jastrab. In this excerpt from the book, we give you Greg's prize-winning contribution, Migrating Serialization Changes in ActionScript.
Contributed by Greg Jastrab (http://www.smartlogicsolutions.com)
This is the winning entry for the Adobe AIR Cookbook Cook-Off contest, sponsored by O’Reilly. The judges—a team of RIA experts—chose this entry because it shows how to accomplish a single task in a clear, concise, and clever manner. It was also selected because of its importance to AIR developers. As developers store instances of classes on the local file system or in the embedded SQLite database, this problem will become even more commonplace. Greg’s solution provides a fix that will be easy for developers to implement. Great job, Greg!
Problem
Your AIR application stores an instance of
a custom IExternalizable class to
disk, and a later update to the application adds new variables to
the class that would cause a runtime error in your readExternal method call if you try to read in the
new variables.
Solution
Include a version marker in your serialization class so that you may parse the data according to the appropriate version that is stored.
Discussion
Consider an AIR application that stores historical weather data
about a town. The data object is written as a class implementing
IExternalizable so that reading and
writing the data is as simple as calling readObject or writeObject. Assume the first version of the
serialization class simply stores the date and a high temperature
of that date.
public class WeatherData implements IExternalizable {
public var date:Date;
public var high:Number;
}
Poor Serialization Choice
Typically, the readExternal and
writeExternal functions would be
implemented in a straightforward manner:
function readExternal(input:IDataInput):void {
date = input.readObject() as Date;
high = input.readFloat();
}
function writeExternal(output:IDataOutput):void {
output.writeObject(date);
output.writeFloat(high);
}
This may seem fine, but what if you want to add the low
temperature for the day in a later version? You could add
low = input.readFloat to the
readExternal function, but because the
data was written to disk without the low data, it would cause a
runtime error when a user who upgraded the application runs it
again.
Migratable Serialization
To avoid this error, you should add a version marker to the beginning of the serialization so that it can be read first in order to determine how the data should be read:
public class WeatherData implements IExternalizable {
namespace wd1_0 = "WD1.0";
namespace wd1_1 = "WD1.1";
protected var version:String;
public var date:Date;
public var low:Number;
public var high:Number;
public function WeatherData() {
version = "WD1.1";
date = new Date();
low = high = 0;
}
public function readExternal(input:IDataInput):void {
version = input.readUTF();
var ns:Namespace = new Namespace(version);
ns::parse(input);
}
public function writeExternal(output:IDataOutput):void {
output.writeUTF(version);
output.writeObject(date);
output.writeFloat(low);
output.writeFloat(high);
}
wd1_0 function parse(input:IDataInput):void {
date = input.readObject() as Date;
high = input.readFloat();
}
wd1_1 function parse(input:IDataInput):void {
date = input.readObject() as Date;
low = input.readFloat();
high = input.readFloat();
}
}
This allows the class to be properly read if the user had run
the initial version of the application that wrote the WD1.0 version
of WeatherData and then ran an updated
version for the first time with this as the WeatherData code.
Adding Members in Future Versions
Now whenever you need to add a field to the class, you need to do the following:
-
Add the variable to the class.
-
Modify the
writeExternalmethod to include that variable in the serialization. -
Increment the version string in the constructor.
-
Add a namespace for this new version.
-
Add a parse function scoped to the namespace you just added and implement
readExternalto correspond to your updatedwriteExternalmethod.
Print
Listen
By 





Sarah, who are you?