I have a service which returns an Array of ObjectProxy objects. I would like to cast this to a custom object (a value object) and create an ArrayCollection. How can I do this?
Edited:
I am using Django and PyAMF for the backend. I had to write a custom SQL query and I am wrapping the resulting records in ObjectProxy and sending the whole result as an ArrayCollection.
Here is my client side code:
[ArrayElementType("SessionVO")]
[Bindable]
private var _list:ArrayCollection;
private function onSessionResultSuccess(event:ResultEvent):void
{
_list = new ArrayCollection(event.result as Array));
}
When I debug, I notice that the elements of event.result are of type ObjectProxy but the _list variable is null. Is there another than to loop over event.result and copy them into _list as SessionVO objects?
If you use the [RemoteClass] tag on your value objects, Flex remoting (Blaze, LCDS) will convert them to your value object for you when sending/returning from a remote service call.
The syntax for RemoteClass is
[RemoteClass(alias="com.co.custom.remote.class")] <--- point to the remote java/php class def
Public Class FooBar
{
public instance variable;
}
Your service can then return an array or hashtable of this class and Flex remoting will convert it for you.
There are cases where objects can become opaque, which you may need to create ObjectProxy code to do custom marshaling, but this is not common. The RemoteClass marshaling can handle very ccomplex object types, subtypes, embedded objects in objects, etc. As long as all of objects on the AS side are typed with RemoteClass, it works as expected.
I'm not sure what you mean by "ObjectProxy objects." I sounds to me like you are already getting returned an array of custom objects.
I recommend looking into some form of AMF gateway. Most serer side languages have an AMF add-on piece to them. It is built into ColdFusion, BlazeDS, and LiveCycle. PHP has ZendAMF and AMFPHP. .NET has FlourineFX and WebORB. Those are just a few examples.
AMF Gateways have a automatic conversion facility; so that the server side object can easily map to a client side object. Here is some info on the RemoteObject tag that describes this. Basically, you specify the RemoteClass metadata on your client side object, and usually specify some form of mapping on the server side object. The AMF Gateway magically handles the rest of the conversion.
In your RemoteObject result handler, you'll just have to convert the returned array to an ArrayCollection. Usually something like this:
var resultArray : Array = event.result as Array;
var resultCollection : ArrayCollection = new ArrayCollection(resultArray);
If you have no control over the server side piece of this application, you may be stuck looping over the results and manually converting them into client side Flex objects.
You can use com.adobe.serializers.utility.TypeUtility;
public function result_handler(event:ResultEvent):void{
var result:Array = TypeUtility.convertListToStrongType(event.result,YourValueObject) as Array;
}
make makeObjectsBindable="false" in the web service property, it will return object.
<s:WebService id="ws" wsdl="http://`xxxx/mobiledata/MobileDataService.asmx?wsdl" fault="fault(event)">
<s:operation
name="GetAll"
resultFormat="object"
result="GetData(event)" makeObjectsBindable="false"
/>
<s:operation
name="Create"
resultFormat="object"
result="SaveData(event)"
/>
</s:WebService>
Related
while calling a java side function from flex side I am having following problem
this works
java side
//in FlexService class
public List<Car> javaFunction(String model){
return carList;
}
flex side
flexService.javaFunction("merc");
while this doesn't work
java side
//in FlexService.java class
public List<Car> javaFunction(String model, List engine){
//some code
return carList;
}
flex side
var engines:ArrayCollection= new ArrayCollection(engineList);
flexService.javaFunction("merc",engines);//function call to java side method
I am getting AMF 16 error.Why I am not able to send ArrayCollection when I can send String.
I referenced http://livedocs.adobe.com/blazeds/1/blazeds_devguide/help.html?content=serialize_data_3.html and it says arraycollection can be received in List..DO I need to create a seperate DTO.??
following is stack trace
2011-10-06 19:57:55,209 83442 [http-8080-5] DEBUG (AbstractUrlHandlerMapping.java:221)- Mapping [/amf] to HandlerExecutionChain with handler [flex.messaging.MessageBroker#13
00800] and 1 interceptor
[BlazeDS]10/06/2011 19:57:55.211 [ERROR] [Endpoint.AMF] Unknown AMF type '16'.
flex.messaging.io.UnknownTypeException: Unknown AMF type '16'.
at flex.messaging.io.amf.Amf3Input.readObjectValue(Amf3Input.java:219)
at flex.messaging.io.amf.Amf3Input.readObject(Amf3Input.java:132)
at flex.messaging.io.amf.Amf3Input.readArray(Amf3Input.java:371)
at flex.messaging.io.amf.Amf3Input.readObjectValue(Amf3Input.java:157)
at flex.messaging.io.amf.Amf3Input.readObject(Amf3Input.java:132)
at flex.messaging.io.amf.Amf3Input.readScriptObject(Amf3Input.java:473)
at flex.messaging.io.amf.Amf3Input.readObjectValue(Amf3Input.java:153)
at flex.messaging.io.amf.Amf3Input.readObject(Amf3Input.java:132)
at flex.messaging.io.amf.Amf0Input.readObjectValue(Amf0Input.java:135)
at flex.messaging.io.amf.Amf0Input.readArrayValue(Amf0Input.java:326)
at flex.messaging.io.amf.Amf0Input.readObjectValue(Amf0Input.java:139)
at flex.messaging.io.amf.Amf0Input.readObject(Amf0Input.java:95)
at flex.messaging.io.amf.AmfMessageDeserializer.readObject(AmfMessageDeserializer.java:226)
at flex.messaging.io.amf.AmfMessageDeserializer.readBody(AmfMessageDeserializer.java:205)
at flex.messaging.io.amf.AmfMessageDeserializer.readMessage(AmfMessageDeserializer.java:125)
at flex.messaging.endpoints.amf.SerializationFilter.invoke(SerializationFilter.java:114)
at flex.messaging.endpoints.BaseHTTPEndpoint.service(BaseHTTPEndpoint.java:278)
at flex.messaging.endpoints.AMFEndpoint$$EnhancerByCGLIB$$8d427d7d.service(<generated>)
at
At the first glance the error cause seems to be the usage of the Vector class. Are you sure that you are using an ArrayCollection and not a Vector on the flex side? Vector serialization was introduced only in BlazeDS 4.5
I am not 100% sure. But you may need to specify generics on the List.
//in FlexService.java class
public List<Car> javaFunction(String model, List<Engine> engine){
//some code
return carList;
}
Also be sure that all the elements in the ArrayCollection in flex have a matching java class.
ArrayCollection is a very heavy class for transfering with lots of unnecessary fields. Use Array instead.
In my attempt to learn flex remoting I came across this
flexService.getRules.addEventListener(ResultEvent.RESULT, loadRules);
here flexService is a remote java object .. In above function call can any one help me that when ResultEvent.RESULT will occur. On studying about ResultEvent in AS document it states as
The event that indicates an RPC operation has successfully returned a result
So keeping that in mind my guess is ResultEvent will be fired when flexService.getRules method will successfully return a list of object,where flexService is object of remote class FlexService having getRules function which returns list of object, Can any one please tell how exactly it works..
Also can some one plz tell me how eventListener can be added to a list of object
PS: I am using Spring as backend
Here you set result to arraycollection
private function loadRules(event:ResultEvent):void
{
var list:ArrayCollection = new ArrayCollection();
list = event.result as ArrayCollection;
}
I'll be going on assumptions since you apparently aren't keen on showing more code or giving pertinent information.
I'm assuming that 'flexService' is a RemoteObject that has set all required properties (destination, endpoint, etc)
I'm assuming that 'getRules' is an available function on your java remote class which returns the information needed.
I'm assuming that everything is being sent over using AMF.
in that case, it's as simple as doing this:
var token:ASyncToken = flexService.getRules(arg1, arg2);
token.addResponder(new Responder(yourResultFunction, yourFaultFunction));
private function yourResultFunction(data:Object):void
{
// Do something with data here
}
private function yourFaultFunction(fault:Object):void
{
// do something if a fault happens
}
Of course, this is very basic and you should try to implement a better pattern (commands) around it.
So, there are a wealth of Flex articles online about how to handle a .NET WebMethod that returns a DataSet or DataTable. Here is an example:
Handling web service results that contain .NET DataSets or DataTables
So, I know how to use result.Tables.<tablename>.Rows and the like. But what I cannot seem to figure out or find online is how to go the other direction - a method to pass objects or tables back to the .NET Webservice from Flex, without stooping to passing XML as a string, or making huge web service methods that have one parameter for each property/column of the object being stored. Surely others, smarter than I, have tackled this issue.
I am using ASP.NET 2.0 Typed DataSets, and it would be really nice if I could just pass one object or array of objects from Flex to the web service, populate my Typed DataTable, and do an Update() through the corresponding typed TableAdapter. My dream would be a [WebMethod] something like one of these:
public void SaveObject(TypedDataTable objToSave) { ... }
public void SaveObject(TypedDataSet objToSave) { ... }
I've had the typed datatables saving to the database, I know how to do that part and even a few tricks, but we had XML being passed back-and-forth as a string - eww. I'm trying to get to a more object-based approach.
The best object based approach is AMF. I assume its probably a bit late in your your development cycle to change your integration layer, but otherwise I dont know of a way to get around marshalling your object(s) back into XML or separating them out into their primitive components.
For .NET implementations of AMF check out:
FlourineFX(FOSS)
WebORB for .NET
Its amazing how easy things become once AMF is used, for example using the Mate MVC framework and an AMF call passing a complex object to the server looks something like this:
<mate:RemoteObjectInvoker instance="yourWebservice" method="saveComplexObject" showBusyCursor="true" >
<mate:resultHandlers>
<mate:CallBack method="saveComplexObjectSuccess" arguments="{[resultObject]}" />
</mate:resultHandlers>
<mate:faultHandlers>
<mate:MethodInvoker generator="{DataManager}" method="presentFault" arguments="{fault}" />
</mate:faultHandlers>
</mate:RemoteObjectInvoker>
With result and fault handlers being optional.
The direction I ended up going was close to what I hoped was possible, but is "hack-ish" enough that I would consider SuperSaiyen's suggestion to use AMF/ORM a better solution for new/greenfield projects.
For sake of example/discussion, let's say I am working with a Person table in a database, and have a typed DataSet called PeopleDataSet that has PersonTableAdapter and PersonDataTable with it.
READ would look like this in .NET web service:
[WebMethod]
public PeopleDataSet.PersonDataTable GetAllPeople() {
var adapter = new PersonTableAdapter();
return adapter.GetData();
}
... which in Flex would give you a result Object that you can use like this:
// FLEX (AS3)
something.dataProvider = result.Tables.Person.Rows;
Check out the link I put in the question for more details on how Flex handles that.
CREATE/UPDATE - This is the part I had to figure out, and why I asked this question. The Flex first this time:
// FLEX (AS3)
var person:Object = {
PersonID: -1, // -1 for CREATE, actual ID for UPDATE
FirstName: "John",
LastName: "Smith",
BirthDate: "07/19/1983",
CreationDate: "1997-07-16T19:20+01:00" // need W3C DTF for Date WITH Time
};
_pplWebSvcInstance.SavePerson(person); // do the web method call
(For handling those W3C datetimes, see How to parse an ISO formatted date in Flex (AS3)?)
On the .NET web service side then, the trick was figuring out the correct Type on the web method's parameter. If you go with just Object, then step into a call with a debugger, you'll see .NET figures it is a XmlNode[]. Here is what I figured out to do:
[WebMethod]
public int SavePerson(PeopleDataSet p) {
// Now 'p' will be a PeopleDataSet with a Table called 'p' that has our data
// row(s) (just row, in this case) as string columns in random order.
// It WILL NOT WORK to use PeopleDataSet.PersonDataTable as the type for the
// parameter, that will always result in an empty table. That is why the
// LoadFlexDataTable utility method below is necessary.
var adapter = new PersonTableAdapter();
var tbl = new PeopleDataSet.PersonDataTable();
tbl.LoadFlexDataTable(p.Tables[0]); // see below
// the rest of this might be familiar territory for working with DataSets
PeopleDataSet.PersonRow row = tbl.FirstOrDefault();
if (row != null) {
if (row.PersonID > 0) { // doing UPDATE
row.AcceptChanges();
row.SetModified();
}
else { // doing CREATE
row.CreationDate = DateTime.UtcNow; // set defaults here
row.IsDeleted = false;
}
adapter.Update(row); // database call
return row.PersonID;
}
return -1;
}
Now, the kluge utility method you saw called above. I did it as extension method, that is optional:
// for getting the Un-Typed datatable Flex gives us into our Typed DataTable
public static void LoadFlexDataTable(this DataTable tbl, DataTable flexDataTable)
{
tbl.BeginLoadData();
tbl.Load(flexDataTable.CreateDataReader(), LoadOption.OverwriteChanges);
tbl.EndLoadData();
// Probably a bug, but all of the ampersand (&) in string columns will be
// unecessarily escaped (&) - kluge to fix it.
foreach (DataRow row in tbl.Rows)
{
row.SetAdded(); // default to "Added" state for each row
foreach (DataColumn col in tbl.Columns) // fix & to & on string columns
{
if (col.DataType == typeof(String) && !row.IsNull(col))
row[col] = (row[col] as string).Replace("&", "&");
}
}
}
It's too complicate to explain but I'll give you an example
I have an AS3 ResultEvent Object
and this object has several propeties which can be accessed by this like:
event.result.name or event.result.age .....
and, I have this String variable: eventProperty:String that contains "name" or "age"
How do I access to event.result. with the variable?
thank you.
The ActionScript (or ECMAScript) . operator is just syntactic sugar, useful but not really needed. For what you want to do you can use the normal object property access operator [].
So you have to do it like this event.result[ eventProperty ].
Good luck,
Alin
You should probably move your ResultEvent object into an actual Object type first. Then you can access the properties via the object. If your object is an arraycollection, make sure to immediately pass the ResultEvent into an arraycollection since you cannot cast it as you normally might (ArrayCollection)ResultEvent. Here is how to throw the result into an object:
var yourObjectName:Object = event.result;
and here is how to throw it into an arraycollection if you need to:
var yourArrayCollection:ArrayCollection = event.result as ArrayCollection;
If I send remote data from Zend_Amf to Flex, if two array properties on the object have the same data values they are deserialized at the remote end with the same memory storage.
Example: AS3 object:
Snippet:
[RemoteClass(alias="TestVO")]
public class TestVO
{
public var test1:Array;
public var test2:Array;
}
When this receives remote data from Zend_Amf server, if the array data are identical it allocates the same storage to the two arrays.
Eg: From remote (ZendAMF) object I send:
$this->test1 = array("foo", "bar");
$this->test2 = array("foo", "bar");
When I debug the TestVO object in Flex debugger I get:
test1 Array(#597d779)
test2 Array(#597d779)
ie: they reference the same array object.
If I send from the remote server slightly different values for the 2 arrays:
$this->test1 = array("foo", "bar");
$this->test2 = array("bar", "foo");
In the Flex debugger the TestVO object now has two seperate arrays as you'd expect:
test1 Array(#54cb7e9)
test2 Array(#54cb741)
AMF output looks Ok, it always sends two seperate values for test1/test2 even if they have the same values, so I'm guessing it's the way Flex de-serializes this?
Any ideas? Thanks.
AMF does this to gain some compression over the wire. If you don't want this then you can switch to the AMF0 format instead of AMF3. But I'm not sure how to do that with ZendAMF.
Found bug ZF-7634 on Zend Framework implementation of AMF. It is serializing the arrays incorrectly.
http://framework.zend.com/issues/browse/ZF-7634