Serialize class with version? - actionscript-3

If I serialize a class in AS3:
var obj:CustomClass = new CustomClass();
var bytes:ByteArray = new ByteArray();
bytes.writeObject(obj);
Lets say I store those bytes in a database and retrieve them at a later date:
var obj:CustomClass = (CustomClass)bytes.readObject();
What if I made a change to that class in that time. That cast would fail. Is there anyway I could differentiate between the 2 ByteArrays and determine that a change to the class has been made?

Related

How do I serialize Vector.<Customobject>?

http://jacksondunstan.com/articles/1642 I followed this to the T, yet I'm running into issues. I'm trying to save a bunch of icons. They have a custom class "Iconn" and are stored in a Vector.<Iconn> for future use. Once the user adds a new icon, I open up a filestream and use it to write the entire vector.
public function addShortcut(f:File):void
{
//Display on side
icons.reverse(); //Add to the front so it displays on the top.
icons.push(new Iconn(f)); //Use 16x16 bitmap
addChild(icons[icons.length - 1]);
icons.reverse();
//Save object
fs = new FileStream();
fs.open(shortcuts, FileMode.WRITE);
fs.writeObject(icons);
fs.close();
reorder(); //Reorganizes the icons on the screen.
}
This works all and well with no errors, but when I try to re-launch the application with some icons saved, the vector doesn't even exist.
icons = new Vector.<Iconn>();
if (shortcuts.exists)
{
trace("Shortcuts exist..adding");
fs = new FileStream();
fs.open(shortcuts, FileMode.READ);
icons = fs.readObject() as Vector.<Iconn>;
fs.close();
trace("icons = " + icons); //TypeError: Error #1009: Cannot access a property or method of a null object reference.
trace("icons length = " + icons.length); //TypeError: Error #1009: Cannot access a property or method of a null object reference.
}
I tried adding registerClassAlias("Vector.<Iconn>", Vector.<Iconn>);, but then I get a compiler error
1067: Implicit coercion of a value of type __AS3__.vec:Vector.<Iconn> to an unrelated type Class.
Edit: Here is my Iconn class http://pastebin.com/5TujzpvR
There's a few reasons this isn't working.
When objects are unserialized, you cannot pass parameters to their constructors. This means that you either need to take out all constructor arguments of ALL classes nested inside your object, or make them all optional by giving them default values.
You have to register every single non-primitive class you use in the object being serialized (including the object's class itself). This includes nested classes. In your case, this includes at least:
flash.display.Bitmap;
flash.display.Sprite;
flash.events.MouseEvent;
flash.filesystem.File;
fl.transitions.Tween;
fl.transitions.easing.Strong;
Plus anything referenced inside of those classes.
Serializing display objects just doesn't work* (it is technically possible with a custom class that implements IExternalizable, but it's quite involved). If you want an example, there is one here.
The best way to serialize display objects, is to create a very very basic class that contains the bare minimum data needed to reconstruct your display object.
In your case, it looks like all you really need is bitmap data. So you could make a simple class like the following:
package
{
import flash.geom.Rectangle;
import flash.utils.ByteArray;
public class SaveData
{
public var imgBytes:ByteArray;
public var bounds:Rectangle;
public var transparent:Boolean;
}
}
All it stores is the raw bitmap data (a byte array of pixel values), and bounds of the bitmap.
First, you need to register all the classes used. That would look like this:
//you need to register your class that you're using writeObject on
registerClassAlias("SaveData", SaveData);
//you also need to register the two classes that are referenced in your SaveData class
registerClassAlias("flash.utils.ByteArray", ByteArray);
registerClassAlias("flash.geom.Rectangle", Rectangle);
//you ALSO need to register Point since it's referenced inside the Rectangle class
registerClassAlias("flash.geom.Point", Point);
Now, to save it, do mostly what you're already doing:
//create a vector to store your list of items to save
var saveArray:Vector.<SaveData> = new Vector.<SaveData>();
//loop through all your icons and do the following in the loop (where bitmap is the icon's bitmap):
var saveData:SaveData = new SaveData();
saveData.bounds = bitmap.getBounds(bitmap);
saveData.imgBytes = bitmap.bitmapData.getPixels(saveData.bounds);
saveData.transparent = bitmap.bitmapData.transparent;
saveArray.push(saveData);
//write the Vector to the file
fs = new FileStream();
fs.open(shortcuts, FileMode.WRITE);
fs.writeObject(saveArray);
fs.close();
Now, when you're ready to load it all back in:
fs = new FileStream();
fs.open(shortcuts, FileMode.READ);
var saveArray:Vector.<SaveData> = fs.readObject() as Vector.<SaveArray>;
fs.close();
//now loop through your array and re-create all your icons.
for(var i:int=0;i<saveArray.length;i++){
var bmd:BitmapData = new BitmapData(saveArray[i].bounds.width, saveArray[i].bounds.height, saveArray[i].transparent);
bmd.setPixels(saveArray[i].bounds, saveArray[i].imgBytes);
var bitmap:Bitmap = new Bitmap(bmd);
//instead of passing in a file to your Iconn class, tweak it so you pass in a bitmap:
var icon:Iconn = new Iconn(bitmap);
//do what you need to do with he icon
}

Flex Mobile AS3, PersistenceManager storing/receiving BitmapData

I'm using the PersistenceManager to store data from my App. If I store BitmapData, it will stored correctly, but after a restart the BitmapData is now an Object. If I cast the Object to BitmapData it doesn't work.
pm.setProperty("sign", signArea.getBitmapData());
And this is the way I try to load it.
pm.getProperty("sign") as BitmapData;
If I doesn't stop the app, it would loaded correctly, but after a restart the "sign" is not BitmapData anymore. It's now an Object.
I don't think you can safely store an instance of BitmapData in a shared object (internally used in the PersistanceManager). It is not explicitly mentioned in the docs: http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/net/SharedObject.html
You can however save the data of the BitmapData as a ByteArray and convert is back when retrieving.
// write
var byteArray:ByteArray = bitmap.bitmapData.getPixels(bitmap.bitmapData.rect);
so.data.byteArray = byteArray;
so.data.width = bitmap.bitmapData.rect.width;
so.data.height = bitmap.bitmapData.rect.height;
so.flush();
// read
var byteArray:ByteArray = so.data.byteArray;
var bitmapData:BitmapData = new BitmapData(so.data.width, so.data.height);
bitmapData.setPixels(bitmapData.rect, byteArray);
Notice that you'll also need to store the width and the height of the image. You could wrap this in an object so that you have 1 entry in the persistance manager instead of 3. This might be more convenient if you want to store multiple bitmaps.
// write
var bd:BitmapData = signArea.getBitmapData();
var r:Rectangle = bd.rect;
pm.setProperty("sign", {bytes:bd.getPixels(r), width:r.width, height:r.height});
// read
var signData:Object = pm.getProperty("sign");
var bitmapData:BitmapData = new BitmapData(signData.width, signData.height);
bitmapData.setPixels(bitmapData.rect, signData.bytes);

Actionscript 3 Flex 4.5 Serialization/Deserialization issue?

I'm having a particular issue deserializing an object in a Flex 4.5 Mobile project. I've connected to a Webservice fine and populate a ListBox fine. I can select the item and get the details just fine as well but after serializing the object and trying to deserialize it; the Object definition is getting lost somewhere.
I have a variable for when the user selected the request in a List
private var selectedReq:ServiceRequest;
//Here we instantiate the local variable when user select id in ListBox
selectedReq = event.currentTarget.selectedItem as ServiceRequest;
Each Service Request the user chooses to save will call this method.
private function writeServiceRequest():void {
var filename:String = buildFileName();
var file:File = File.applicationStorageDirectory.resolvePath(filename);
if (file.exists)
file.deleteFile();
var fileStream:FileStream = new FileStream();
fileStream.open(file, FileMode.WRITE);
//selectedReq is the private var of the users selected item
fileStream.writeObject(selectedReq);
fileStream.close();
}
When the users want to view the request this method is called.
private function readServiceRequest():ServiceRequest {
var filename:String = buildFileName();
var file:File = File.applicationStorageDirectory.resolvePath(filename);
if (!file.exists) {
return null;
}
var fileStream:FileStream = new FileStream();
fileStream.open(file, FileMode.READ);
var objReq:ServiceRequest = fileStream.readObject() as ServiceRequest;
fileStream.close();
return objReq;
}
The Class Object is similar to.
public var id:uint;
public var requisitioner:String;
public var requestItems:ArrayCollection //Webservice it's actually List<requestItems>
public var requestProcesses:ArrayCollection // WSDL it's actually List<>
When I try to read/deserialize like
//This line is null but the file exist and the object was written
var objReq:ServiceRequest = readServiceRequest() as ServiceRequest;
if(objReq) {
selectedReq = objReq;
}
If I do not cast the readServiceRequest() as ServiceRequest and simply return an Object; I can iterate through the Object and get the correct values returned from the serialized object.
I can only assume the Classes that Flex created from the Webservice may be causing this? If the values are getting written but not the object type then something has to be lost in the serialization - correct?
Sorry for all the details but I'm a little lost at this time.....any help would be appreciated.
RL
var objReq:ServiceRequest = readServiceRequest() as ServiceRequest;
The above line will continue to return null.
I bet that if you modify it in the following way:
var objReq:ServiceRequest = ServiceRequest(readServiceRequest());
You'll get a run-time exception with a message similar to Can't cast ObjectProxy to ServiceRequest
If that's the case, then the reason you get this is because the AMF serializer doesn't preserve the type information when serializing the ServiceRequest-instance.
In order to fix this you need to call flash.net.registerClassAlias() before the serialization/deserialization.
flash.net.registerClassAlias("fully.qualified.name.ServiceRequest", ServiceRequest);
Try if this work.
Check if event.result is ByteArray.
Read/store the event.result as ByteArray.
var byteArray:ByteArray = event.result as ByteArray;
Get/Deserialize Object using readObject() function.
var object:Object = byteArray.readObject();
Cast to targetted object type. ServiceRequest in your case.
var tartgettedObject:ServiceRequest = object as ServiceRequest;
or
var tartgettedObject:ServiceRequest = ServiceRequest(object);
My bad for not signing in before posting the question.
I did use registerClassAlias which did not help. I did find on the Adobe forum other simular issues and the constant feedback was the problem is most likely caused by the Object Class loosing it's attributes during the file write reason being the Class Objects are written when the Datasource is created from the Webservice. Since my original question we decided to use SQLite which is working perfectly fine - thanks for all you help.
RL

How do you combine two bytearrays in AS3?

I'm trying to combine two ByteArrays to send it out as post data using URLRequest.. Whenever I try to simply add them up, the request becomes GET instead of POST and the data for some reason doesn't get included.
create a total ByteArray by adding other ByteArray objects to it via the writeBytes() public method of the ByteArray class.
more info here: Reading and writing a ByteArray
Combining / Concatination Two Byte Arrays
Var Data:ByteArray = new ByteArray();
Var Salt:ByteArray = new ByteArray();
var DataAndSalt:ByteArray = new ByteArray();
DataAndSalt.length = (Data.length + Salt.length);//Defines the **length of Resultant Array**
//Array Copy Method(VB)/ Concate the ByteArray(ActionScript) one After another
DataAndSalt.writeBytes(Data);
DataAndSalt.writeBytes(Salt);
I will Show here Conversion of String into Byte Array and Merging Them (concate /combining) them into single byte array
// In Detail
var HashOut:ByteArray = new ByteArray();
var byterrData:ByteArray = new ByteArray();
var byterrSalt:ByteArray = new ByteArray();
//conversion of string Data and Salt in respective (UTF-8 and Default) Byte Array
var Data:String = "password";
var Salt:String ="‰ô©³¶í"; //Using Special Characters in a String variable
byterrData.writeMultiByte(Data, "iso-8859-1");
byterrSalt.writeMultiByte(Salt,Salt);
var DataAndSalt:ByteArray = new ByteArray();
DataAndSalt.length = (Data.length + Salt.length);
// Concate the ByteArray
DataAndSalt.writeBytes(Data);
DataAndSalt.writeBytes(Salt);
//Now You can Trace It by using
trace(DataAndSalt[0]);
trace(DataAndSalt[index Number]);
Not sure what your code looks like... the GET/POST issue is very weird.
However, use the below instead of trying to "add them up" (whatever that means).
array3 = array1.concat(array2);

Dynamically Instancing Objects ActionScript 3.0

I have a variable named "type". And I want to instance an object with the name of the value of type. Here is an example:
var myObjectName = "ball";
var object = new ball(); //Except I want to use the value of myObjectName.
I believe this used to be easy with AS2 when using _global, but I'm not sure how to do it in AS3?
Any help?
First get the class object with flash.utils.getDefinitionByName(), then instantiate that object:
var myClass:Class = getDefinitionByName(myObjectName) as Class;
var object:Object = new myClass();
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/utils/package.html#getDefinitionByName()