Morten Gorm Madsen
Morten Gorm Madsen

Reputation: 81

How to deal with data inconsistency? AS3 IExternalizable.readExternalData + sqlstatement.execute()

I do AS3 Air development and use SQLite to store objects in between sessions which is well documented in the AS3 Developer Guide as well as the AS3 SDK Reference.

The classes stored in the database implement IExternalizable so that the instances can be stored and restored using BLOBs. This works out well but I have difficulties finding out how to properly react to faulted deserialization. The problem occurs in the following situation:

  1. Object serialized
  2. Object saved
  3. Class changed and program updated
  4. Serialized data no longer matches saved data

My initial approach is something like this:

public class SampleClass implements IExternalizable
{
    public static const version:int = 1; //increments upon program update
    public var idata:int;

    public function readExternal(data:IDataInput):void{
        if(version != data.readInt()) //check for corrupted data
            throw new IOError();

        idata = data.readInt();
    }

    public function writeExternal(data:IDataOutput):void{
        data.writeInt(version);
        data.writeInt(idata);
    }
}

Throwing an error in readExternal sadly causes my SQLStatement.execute() to fail horribly and try..catch is of no use.

My current ideas to deal with the issue:

  1. Erase all data from database when program is updated (ouch)
  2. Add flag to class to signal faulty deserialization
  3. Make SQLStatement.execute() react properly to thrown error

1 and 2 I know how to do but neither are pretty. 3 I have found no solution to (yet?)

Other ideas or knowhow appreciated.

Upvotes: 0

Views: 230

Answers (1)

Hi this is documented in the Adobe AIR 1.5 Cookbook (9.13 Migrating Serialization Changes in ActionScript) Contributed by Greg Jastrab he suggests to use namespaces for version changes as in following code sample:

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();
    }
}

I've used this solution and it works as promised:) and following is "algorithm" for adding new members in future versions

  1. Add new variable
  2. Modify writeExternal to include it in the serialization
  3. Increment version string in constructor
  4. add new namespace for new version
  5. add a parse function scoped to the new namespace and implemend readExternal to match modified writeExternal

best regards

Upvotes: 2

Related Questions