O'Reilly FYI

News from within O'Reilly

The Adobe AIR Cookbook Cook-Off Contest Winning Entry Posted Here

 
By Sara Peyton
November 21, 2008 | Comments: 1
9780596522506_cat.gif

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!

aacb_badge.png

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:

  1. Add the variable to the class.

  2. Modify the writeExternal method to include that variable in the serialization.

  3. Increment the version string in the constructor.

  4. Add a namespace for this new version.

  5. Add a parse function scoped to the namespace you just added and implement readExternal to correspond to your updated writeExternal method.


1 Comments

Sarah, who are you?

Leave a comment


Popular Topics

Browse Books

Archives

Or, visit our complete archives.

FYI Topics

Recommended for You

Got a Question?