Your AIR application stores a custom IExternalizable class to disk and later update to the application adds new variables to the class which would cause a runtime error in your readExternal() call if you tried to read in the new variables.
Include a version marker in your serialization class so that you may parse the data according to the appropriate version which is stored.
Let's assume you're writing an AIR application that stores historical weather data about a town. You've written the data object as a class implementing IExternalizable so that reading and writing the data is as simple as calling readObject() or writeObject(). Let's assume the first version of the serialization class simply stores the date and a hi temperature of that date.
public class WeatherData {
public var date:Date;
public var hi:Number;
}
Typically, the readExternal and writeExternal functions would be implemented in a straightforward manner:
function readExternal(input:IDataInput):void {
date = input.readObject() as Date;
hi = input.readFloat();
}
function writeExternal(output:IDataOutput):void {
output.writeObject(date);
output.writeFloat(hi);
}
This may seem fine, but what if you want to add the lo temperature for the day in a later version? You could add lo = input.readFloat(); to the readExternal function but since the data was written to disk without the lo data it would cause a runtime error when a user who upgraded the app runs it again.
Add a version marker to the beginning of the serialization so that it may be read first in order to determine how the data should be read:
public class WeatherData {
namespace wd1_0 = "WD1.0";
namespace wd1_1 = "WD1.1";
protected var version:String;
public var date:Date;
public var lo:Number;
public var hi:Number;
public function WeatherData() {
version = "WD1.1";
date = new Date();
lo = hi = 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(lo);
output.writeFloat(hi);
}
wd1_0 function parse(input:IDataInput):void {
date = input.readObject() as Date;
hi = input.readFloat();
}
wd1_1 function parse(input:IDataInput):void {
date = input.readObject() as Date;
lo = input.readFloat();
hi = input.readFloat();
}
}
This allows the class to be properly read if the user had run the initial version of the application which writes the "WD1.0" version of the WeatherData and then ran an updated version for the first time with this as the WeatherData code.
Now whenever you need to add an additional field to the class you simply need to:
writeExternal() to include that variable in the serializationversion string in the constructorreadExternal() to correspond with your updated writeExternal() methodThe example code better illustrates this concept. Included within the ZIP file are 2 Flex Builder Project archives. Import the MigrAIRtion-v1.zip into Flex Builder first and debug the application. Click the "Create Data" button to generate some data and then click the "Read Weather Data" button to read it in. You will only see the date and a hi value in the DataGrid.
Then import the MigrAIRtion-v2.zip file into Flex Builder and run that. Click the "Read Weather Data" button and it will read in the old "WD1.0" version that does not have a lo temperature included in the serialization. You will see in the trace output that if it sees the lo was 0 it will generate a new lo value. The DataGrid now has a lo value column. Click the "Write Weather Data" to overwrite the data w/ the "WD1.1" serialization. Quit the application and re-run it. When you click "Read Weather Data" you will see that the trace output does not say that it is re-generating the lo temperature, since it was properly read in from the 1.1 serialization.
+