Serializing and Deserializing ValueObjects in Flex and AIR

August 15th, 2008

Author: Michael Krotscheck

Category: Tutorials

Tags: , , , ,

This post originally written for Resource Interactive’s Technology Blog.

One of the holy grails of the "Build Once, Deploy Anywhere" promise of the Adobe AIR isn’t simply that your application will cross all major operating systems, but also that the same codebase can be deployed via the web as an RIA. Admittedly, there remain some differences in functionality; For instance, a desktop application has to worry about windows or contextual menus. Another challenge altogether is transferring data between a desktop and a web application, which can be a daunting task if you’re trying to share multiple files, yet is perhaps not as difficult as you might think.

AIR, via the ByteAray and FileStream classes, allows us to take an arbitrary data object and serialize it to our own custom file type. Since the ByteArray class is available within the standard Flex environment as well, the only trick remaining is to make this file accessible to an application deployed via the web. When running in the browser, Flash’s security sandbox prevents us from directly accessing it on the desktop (At least until the release of Flash Player 10, which relaxes that restriction), however we can still send a file to the server and then open it from there. In short, files generated from AIR can be easily deserialized and used within the Flash Player runtime.

Potential uses for this are many. Consider, for instance, that your application has a fairly complex customized display object that draws its information from a value object. This value object contains strings, arrays, and perhaps even an image. By serializing this information into a file, a user can easily share all this information without the need for a data transfer process that has to support multiple file objects. Simply write your file from binary and send it.

Step 1: Create your Value Object

The first step is of course to create your Value Object. The below code should be no surprise to anyone - the only difference is  the [RemoteClass] metadata tag. This tag is a catchall identifier used by Actionscript to map class-to-binary identification, and is used anywhere  serialization occurs: The AMF protocol, SharedObject, ByteArray Serialization, etc.

package com.resource.vo
{
	/**
	 * A simple data Value Object to show that it is possible
	 * to deserialize a binary object created in AIR within the
	 * Flex environment.
	 */
	[RemoteClass(alias="com.resource.vo.SimpleDataVO")] 
	public class SimpleDataVO
	{
		[Bindable]
		public var firstName : String = "";
		[Bindable]
		public var lastName : String = "";
		[Bindable]
		public var email : String = "";
	}
}

Step 2: Save your Value Object to a File

The code shown in the next two steps is almost exactly the same, except that the button handlers are a little different. As you will note, this is an AIR application in which we create a simple form that writes a constructed data object to a specified location on the file system when the Save button is clicked.

<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
	<mx:Script>
		<![CDATA[
			import com.resource.vo.SimpleDataVO;
			import flash.filesystem.*;

			[Bindable]
			private var dataObject : SimpleDataVO;
			
			override protected function initializationComplete():void
			{
				super.initializationComplete();
				
				dataObject = new SimpleDataVO();
			}
			
			private function saveFileButtonHandler ( event : MouseEvent ) : void
			{
				// First, generate your ByteArray from the VO.
				var byteArray : ByteArray = new ByteArray();


				byteArray.writeObject( dataObject );
				
				// Resolve your file location.
				var file : File = File.applicationStorageDirectory.resolvePath( "testFile.ri" );
				var fileStream:FileStream = new FileStream();
				
				// Save the file to the given location.
				fileStream.open(file, FileMode.WRITE);
				fileStream.writeBytes( byteArray );
				fileStream.close();
			}
			
		]]>
	</mx:Script>
	<mx:Form x="10" y="10" width="363" height="196">
		<mx:FormItem label="First Name">
			<mx:TextInput id="firstNameInput" text="{dataObject.firstName}" change="dataObject.firstName = firstNameInput.text"/>
		</mx:FormItem>
		<mx:FormItem label="Last Name">
			<mx:TextInput id="lastNameInput" text="{dataObject.lastName}" change="dataObject.lastName = lastNameInput.text"/>
		</mx:FormItem>
		<mx:FormItem label="Email">
			<mx:TextInput id="emailInput" text="{dataObject.email}" change="dataObject.email = emailInput.text"/>
		</mx:FormItem>
		<mx:FormItem>
			<mx:Button label="Save" click="saveFileButtonHandler(event)" />
		</mx:FormItem>
	</mx:Form>
</mx:WindowedApplication>

Step 3: Read your Value Object

This next application written for web deployment, and matches the above in all but the button handler. Here we use the URLLoader and URLRequest classes to retrieve our file from an upload directory. Once it’s been received we access the returned data and let the ByteArray class reconstruct our logical object.

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
	<mx:Script>
		<![CDATA[
			import com.resource.vo.SimpleDataVO;
			
			[Bindable]
			private var dataObject : SimpleDataVO;
			
			private function loadFileButtonHandler ( event : MouseEvent ) : void
			{
				// Execute the file load.
				var request : URLRequest = new URLRequest ( "upload/testFile.ri" );
				var receptor : URLLoader = new URLLoader( request );
				
				// Make sure our content is interpreted as a ByteArray.
				receptor.dataFormat = URLLoaderDataFormat.BINARY;
				receptor.addEventListener( Event.COMPLETE, fileLoadedHandler );
			}
			
			private function fileLoadedHandler ( event : Event ) : void
			{
				// Retrieve the event target, cast as the URLLoader we just created
				var loader : URLLoader = event.target as URLLoader;
				
				// Retrieve the loaded data. We know it's a ByteArray, so let's cast it as well.
				var data : ByteArray = loader.data as ByteArray;
				
				// Use the ByteArray.readObject method to reconstruct the object.
				var obj : Object = data.readObject();
				
				// Cast the object and assign it to our data container.
				dataObject = obj as SimpleDataVO;
			}
		]]>
	</mx:Script>
	<mx:Form x="10" y="10" width="363" height="196">
		<mx:FormItem label="First Name">
			<mx:TextInput id="firstNameInput" text="{dataObject.firstName}" change="dataObject.firstName = firstNameInput.text"/>
		</mx:FormItem>
		<mx:FormItem label="Last Name">
			<mx:TextInput id="lastNameInput" text="{dataObject.lastName}" change="dataObject.lastName = lastNameInput.text"/>
		</mx:FormItem>
		<mx:FormItem label="Email">
			<mx:TextInput id="emailInput" text="{dataObject.email}" change="dataObject.email = emailInput.text"/>
		</mx:FormItem>
		<mx:FormItem>
			<mx:Button label="Load" click="loadFileButtonHandler(event)" />
		</mx:FormItem>
	</mx:Form>
</mx:Application>

A few caveats

For ByteArray to properly serialize and deserialize your object, two things must be the case. First, you must use the [RemoteClass(alias='')] metadata tag. Secondly, you have to make sure your value object is compiled into your application. This can be done either implicitly (via the type declaration I used above), or explicitly by adding your class to the mxmlc compiler arguments.

Additionally, there are a few things  I explicitly omit in this tutorial. Foremost of these is the send/receive architecture that would ensure the proper distribution of a file created from AIR. This will vary by implementation, so I’m not going to tell you how this should or should not be done. The other is that I don’t discuss the creation of ByteArrays within Flex and returning them to AIR. Since the Flash Player doesn’t yet allow you to save to the desktop without bouncing through a server, the implementation of Save-From-Flash can be complex and involved, and will have to wait for a future article.

Share and Enjoy:
  • Digg
  • del.icio.us
  • Facebook
  • Google

3 Comments

Comment by Paolo Carraro on 2008-12-12 04:43:20

I use this way to save data locally in app i made. I save a hierarchy of value objects and all VO custom classes are tagged with [RemoteClass (alias="...")].

But i’ve found that sometimes data loose part of it.
For example: I save my MainValueObject that contains others VOs and so on… when than i re-open the file sometimes happen some piece of data is lost! I’m trying to descover and fix the bug but i really don’t know where start… Can you suggest me something to check?

I’m very interesting about this topic and I’ll open soon a blog to share this and other code and problems. Thank you for your contribute

Comment by Paolo Carraro on 2008-12-30 06:20:54

Hi, me again! here my post about the topic… http://www.ekr.it/PC/blog/

You may use <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> in your comment.