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
Related
I'm trying to modify an Object in another class. So I have something like this:
MainClass.as:
..
var myObject:Object = new Object();
Class2_Instance.get_JSON(myObject);
trace(myObject.id); // output: undefined. whereas it should be 42. see below.
..
Class2.as
public function get_JSON(url:String , the_object:Object)
{
var request:URLRequest = new URLRequest(url);
var variables:URLLoader = new URLLoader();
variables.dataFormat = URLLoaderDataFormat.TEXT;
variables.addEventListener(Event.COMPLETE, Complete_Handler_JSON(the_object));
try
{
variables.load(request);
}
catch (error:Error)
{
trace("Unable to load URL: " + error);
}
}
function Complete_Handler_JSON(the_object:Object):Function
{
return function(event:Event):void
{
var loader:URLLoader = URLLoader(event.target);
the_object = JSON.parse(loader.data);
trace(the_object.id); //returns 42.
};
}
So the JSON operation performs correctly within Class2, and it assigns the .parse() value to the_object, but I think there is something I don't understand with AS's pass-by-reference logic. Since I was expecting myObject in MainClass.as would change, too.
What should I do to modify the value of the function argument (myObject) directly ?
Thanks !
The issue is in this line in your event handler:
the_object = JSON.parse(loader.data);
The minute you do this, you're no longer dealing with the object which you passed in to the method. You're assigning a new value to the local variable named the_object. It's important to understand that objects are not passed by reference - object references are passed by value. So the_object in your method is a copy of the reference. When you assign a new value, that copy is overwritten with a different reference.
The solution is to pass the reference by reference - this is related to the concept of double pointers in languages such as C++, also known as double indirection. I'm not certain that this is even possible in ActionScript.
A better solution, just return the deserialized object as a return value.
Objects are passed by reference.
But as i see in your code you are overwriting the_object by another one on the line : the_object=JSON.parse(loader.data).
You can create a new variable and then copy the values in the_object:
var json:Object = JSON.parse(loader.data);
for (var k:String in json) {
the_object[k]=json[k];
}
I want to pass an object to a function by value so that I can make modifications to that object. I don't want the original object to be updated. However, all the function parameters are passed by reference.
I've tried to copy an object ( var new_object:Object = original_object; ) This just creates a pointer to original_object.
Is there a way I can pass parameter by value?
update One workaround I see is to make deep copy of an object by using ByteArray as described here. Not sure how efficient it is. Maybe there is a better solution out there.
You will have to make a copy of the object before passing it to the function :
public function copy(value:Object):Object
{
var buffer:ByteArray = new ByteArray();
buffer.writeObject(value);
buffer.position = 0;
var result:Object = buffer.readObject();
return result;
}
public function testFunction(obj:Object):void
{
//do something with obj
}
public function test():void
{
var obj:Object = {};
testFunction(copy(obj));
}
I've been trying to learn flex/flash programming and am working on a project where I need to populate a spinner list in flex dynamically from a string. I have a function that separates the string using "split" and now I need to populate an array list. I have been working with this stupid big for hours now and can;t find help anywhere. I keep getting the following error:
TypeError: Error #1009: Cannot access a property or method of a null object reference.
at views::CommonPlaces/initApp()[/Users/twing207/Documents/Adobe Flash Builder 4.6/AmericanTaxi/src/views/CommonPlaces.mxml:30]
My code is here:
<fx:Script>
<![CDATA[
import mx.collections.ArrayList;
import spark.events.IndexChangeEvent;
var Arr1:Array;
var Arr2:Array;
var Arr3:Array;
[Bindable]
public var CommonPlacesArray:ArrayList;
var CommonPlacesData:String = new String("2133664:American Taxi Dispatch, Inc:Mount Prospect:834 E RAND RD|2133665:Walmart:Mount Prospect:930 Mount Prospect Plaza|2228885:Garage:Des Plaines:1141 Lee St|2228886:Asian Island:Palatine:1202 E Dundee Rd|2229464:Kohl's:Arlington Heights:700-856 W Dundee Rd|");
var CurrentSelect:String = new String();
private function initApp():void {
Arr1 = CommonPlacesData.split("|");
var arrLength:Number = new Number(Arr1.length);
for (var i:Number = 0; i < (arrLength - 1); i++) {
CurrentSelect = new String(Arr1[i]);
Arr2 = CurrentSelect.split(":");
//THE LINE BELOW IS WHERE IT STOPS:
CommonPlacesArray.addItem(Arr2[1]);
}
}
It doesn't seem to like the "CommonPlacesArray.addItem" line. Any help or a point in the right direction would be great. Thanks in advanced!
On another note, I am also getting the error: "Access of undefined property: data" on the following:
Here in another view I set the value for data.UserCommonReturnData to a string.
function LoginLoaded (e:Event):void {
trace(e.target.data);
var ServerReturn:String;
ServerReturn = new String(e.target.data);
data.UserCommonReturnData = ServerReturn;
navigator.pushView(CommonPlaces, data);
}
and here I try to pull it back:
var CommonPlacesData:String = new String();
var CurrentSelect:String = new String();
//The next line gives the error:
CommonPlacesData = data.UserCommonReturnData;
Any idea??
You never construct CommonPlacesArray, you just declare it.
var CommonPlacesArray:ArrayList = new ArrayList();
If you check the Array List API you can also clearly see it has a constructor that accepts an array, meaning you can copy the data to it without having to iterate over it yourself.
What I'm trying to do:
-Have objects in a toolbar, drag and dropable onto a movieclip (they then become a child of the movieclip). Once this is done, I want to be able serialize this object, so I can save it to a file. Then, I can reload this file, and continue draging/dropping things onto/off of this movieclip.
How I'm doing it:
public class Serialization {
public static function serializeToString(value:Object):String{
if(value==null){
trace("null isn't a legal serialization candidate");
}
var bytes:ByteArray = new ByteArray();
bytes.writeObject(value);
bytes.position = 0;
var be:Base64Encoder = new Base64Encoder();
be.encode(bytes.readUTFBytes(bytes.length));
return be.drain();
}
public static function readObjectFromStringBytes(value:String):Object{
var dec:Base64Decoder=new Base64Decoder();
dec.decode(value);
var result:ByteArray=dec.drain();
result.position=0;
return result.readObject();
}
}
This is where call the function/write it to the file:
var fr:FileReference = new FileReference;
fr.addEventListener(Event.COMPLETE, success);
var txtString:String = new String();
txtString = save.Serialization.serializeToString(pagePic);
trace(txtString);
fr.save(txtString, "test.txt");
Unfortunately, txtString appears to be blank. Am I approaching this wrong?
Side notes:
This is being developed for a mobile platform.
Unfortunately MovieClips, Sounds, and other resources cannot be serialized. My solution is to create a custom class that will store all my properties and reassign them upon loading, or just write to/parse a text file when saving/loading.
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()