How to create a generic function for parsing an XML into an Array Collection of Objects of a certain type
The Problem
Over time, I found that it is more productive and less error prone (at least for me :D) to work with structures of data that are rather collections of typed Objects than collections of Objects or XML. Here are some advantages when you work with typed objects: gets support from Flex Builder tool (code completion, compile time checking), the code is easier to read and maintain, and somehow I think is more elegant.
Back to earth now, let’s say I have a Flex application that reads data from a server and format of the data is XML. For example, my Flex application receives some data like this:
<?xml version="1.0" encoding="utf-8"?> <root> <person> <name>John</name> <age>25</age> </person> <person> <name>Mike</name> <age>27</age> </person> <person> <name>Michele</name> <age>22</age> </person> <person> <name>Julia</name> <age>29</age> </person> </root>
I could use this data like an XML object in my application (for example if I use a HTTPService to bring them, I can set the resultFormat property to “XML”), or I can create an ActionScript class VOPerson:
package org.example {
public class VOPerson {
public var name:String;
public var age:int;
}
}
And I can transform this XML in an Array or Array Collection of VOPerson.
In an AIR application you can save on disk any object you might have. Suppose I have an Array Collection of VOPerson, I can use AIR API to save this object on disk. But, there is an issue: when I read the object from disk back into my application, the type of objects from Array Collection is lost. So instead of an Array Collection of VOPerson, I will have an Array Collection of Objects. This could be a problem for me, as my methods expect a VOPerson object and not a generic Object.
The Solution
The solution is to create a “generic” function that knows how to transform a collection (could be an Array, an Array Collection or a XMLList) of generic objects into an Array Collection of typed Objects. I could use this function when I read serialized objects from disk in AIR or when I use HTTPService to get XML from server in a Flex application. In order to have a simple solution I need to use the same names for the XML nodes and my typed object properties.
Let’s start with the HTTPService and XML code. This service has a property xmlDecode that can be used to set a function that is called right before the result handler gets called. This function must return an Object, and the return object will be passed in the event. result property to the result handler. The XML received from server is passed as an argument to the function, as XMLNode object. Pay attention that this function is called only when you set the resultFormat property to “object”.
So let’s take a look at how the code might look:
<mx:HTTPService id="myService" url="http://corlan.org/downloads/examples/get_data.html" method="POST" resultFormat="object" showBusyCursor="true" xmlDecode="serializeXMLToVO" result="resultListener(event)" fault="faultListener(event)"/>
Let’s define the three functions I set up in the HTTPService object (resultListener, faultListener, serializeXMLToVO):
private function resultListener(event:ResultEvent):void { myCollection = event.result as ArrayCollection; } private function faultListener(event:FaultEvent):void { Alert.show(event.fault.faultDetail, "Error"); } private function serializeXMLToVO(node:XMLNode):Object { var xml:XML = new XML(node); //make an XML object to be able to work with E4X var list:XMLList = xml..person; //get a XML List with all the nodes person //call the generic function that knows to make from a generic collection of dynamic objects //an array collection of value objects return serializeToVO(list, VOPerson); }
As you can see in the resultListener I’ve just saved the event.result cast as ArrayCollection to myCollection variable (I use this variable as data provider for my data grid). So although the server sends me an XML, the result is translated to a proper object. All this transformation is done by the serializeXMLToVO function (this function is called by HTTPService to process the result and after this the ResultEvent is dispatched). In serializeXMLToVO first I create an XML object out of XMLNode (it is far more easy to work with E4X to get my data out of XMLNode) and then I create an XMLList with all the person nodes. The next step is to call my generic function (serializeToVO) and pass to this function my XMLList and the class I want to be used for storing one person node (VOPerson is my class in this case). This function will walk through and will do the “dirty” job. I wanted to use a generic function so I can reuse it whenever I have similar needs and also I can pass any object type I want to use.
Let’s take a look at this function:
private function serializeToVO(collection:Object, claz:Class):ArrayCollection { var arr:ArrayCollection = new ArrayCollection(); //use reflection to find all the members from our claz object var tmp:XML = describeType(new claz()); //save just the variables into am XML List using an E4X expression //(I want to get all the atributes "name" from all the nodes "variable") var properties:XMLList = tmp..variable.@name; //iterate our collection for each (var item:Object in collection) { var temp:Object = new claz(); //iterate our properties and use the values from the current item //to initializa our value object for each(var member:Object in properties) { temp[member] = item[member]; } arr.addItem(temp); //add the item to collection } return arr; //return the array collection }
Let’s take a look at the arguments: we have an Object (collection) which in fact should be an instance of XMLList, Array or ArrayCollection. The other one is a Class object, this is the type I want to be used for saving an item from the collection. Because I don’t know what properties might have the claz object, I need to use reflection and find what members I have in the claz:
var tmp:XML = describeType(new claz()); var properties:XMLList = tmp..variable.@name;
flash.utils.describeType does this reflection; it returns an XML with all the information I need. From this XML using an E4X expression I can create an XMLList with the members I am looking for. Now I need to loop through my collection and for each item I need to loop through my list of members and read the value from the item and write the value into my typed object (this works because I have the same names both for XML and my type objects; I have a node name in the person, and I have a property name in VOPerson). In ActionScript you don’t have iterators, or you can say you have but the iterator is the construct for each (item:object in collection). So in this way I can iterate on any Array, XMLList or ArrayCollection or on an Object.
That’s it for the HTTPService. The code for writing and reading the array collection to disk is much simpler. I have a saveData function and a readData function and in readData I make use again of my function serializeToVO. This time, on the ArrayCollection retrieved from disk.
private function saveData():void { var file:File = File.applicationStorageDirectory.resolvePath(fileName); if (file.exists) file.deleteFile(); var fileStream:FileStream = new FileStream(); fileStream.open(file, FileMode.WRITE); fileStream.writeObject(myCollection); fileStream.close(); Alert.show("the data was saved in " + file.nativePath, "File Path"); } private function readData():void { var file:File = File.applicationStorageDirectory.resolvePath(fileName); if (!file.exists) return; var fileStream:FileStream = new FileStream(); fileStream.open(file, FileMode.READ); var myObject:ArrayCollection = fileStream.readObject() as ArrayCollection; fileStream.close(); myCollection = serializeToVO(myObject, VOPerson); }
Sample Code
As usually :), I created an AIR project with all the code. You can download it from here and you can import it in Flex Builder using Import > Flex Builder > Flex Project. When you run the application, you will see a data grid and four buttons:
- “Get Data” – retrieves and displays the data from server in the Data Grid
- “Save Data Locally” – save the Array Collection to a file on disk
- “Clear data from server” – makes the Array Collection used to store the data from server null
- “Read Data Saved Locally” – retrieves the data from the disk and stores in the Array Collection used as data provider for data grid
If you want to have a quick view on the code:
<?xml version="1.0" encoding="utf-8"?> <mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"> <mx:Script> <![CDATA[ import flash.utils.describeType; import mx.collections.IList; import mx.controls.List; import org.example.VOPerson; import mx.collections.ArrayCollection; import mx.controls.Alert; import mx.rpc.events.FaultEvent; import mx.rpc.events.ResultEvent; [Bindable] private var myCollection:ArrayCollection; //this is collection used to store the data private var fileName:String = "myObject"; //this is file name used to saved locally the data /** * The result listener for the HTTPService. * Saves the result into <code>myCollection</code> array collection * @param event */ private function resultListener(event:ResultEvent):void { myCollection = event.result as ArrayCollection; } /** * Fault listener for HTTPService. It's just displaying the error message * @param event */ private function faultListener(event:FaultEvent):void { Alert.show(event.fault.faultDetail, "Error"); } /** * This function is called by the HTTPService when the result is available * and before the listener for result gets called. Thus I can parse the XML * returned by the server into an Array Collection of value objects VOPerson * @param node * @return an Array collection of VOPerson */ private function serializeXMLToVO(node:XMLNode):Object { var xml:XML = new XML(node); //make an XML object to be able to work with E4X var list:XMLList = xml..person; //get a XML List with all the nodes person //call the generic function that knows to make from a generic collection of dynamic objects //an array collection of value objects return serializeToVO(list, VOPerson); } /** * This is a generic function that can transform a collection of dynamic objects into * an array collection of a certain object type (the given <code>claz</code>) * @param collection to be parsed; could be an array collection or an XMLList or an array * @param claz to be used for storing one item * @return an array collection of <code>claz</code> */ private function serializeToVO(collection:Object, claz:Class):ArrayCollection { var arr:ArrayCollection = new ArrayCollection(); //use reflection to find all the members from our claz object var tmp:XML = describeType(new claz()); //save just the variables into am XML List using an E4X expression //(I want to get all the atributes "name" from all the nodes "variable") var properties:XMLList = tmp..variable.@name; //iterate our collection for each (var item:Object in collection) { var temp:Object = new claz(); //iterate our properties and use the values from the current item //to initializa our value object for each(var member:Object in properties) { temp[member] = item[member]; } arr.addItem(temp); //add the item to collection } return arr; //return the array collection } /** * Save the value of <code>myCollection</code> into a file inside the * local application storage directory */ private function saveData():void { //create the file to store the data var file:File = File.applicationStorageDirectory.resolvePath(fileName); if (file.exists) file.deleteFile(); //use a File Stream to write to file var fileStream:FileStream = new FileStream(); fileStream.open(file, FileMode.WRITE); //write the data as an object to file fileStream.writeObject(myCollection); fileStream.close(); //display the path to the file I wrote it on disk Alert.show("the data was saved in " + file.nativePath, "File Path"); } /** * Read the serialized object on the hard-disk and transform it into * an array collection of VOPerson * Save array collection of VOPerson into the local <code>myCollection</code> * variable */ private function readData():void { //get the file use var file:File = File.applicationStorageDirectory.resolvePath(fileName); if (!file.exists) return; var fileStream:FileStream = new FileStream(); fileStream.open(file, FileMode.READ); var myObject:ArrayCollection = fileStream.readObject() as ArrayCollection; fileStream.close(); //use my generic serializing function to get an array collection of VOPerson myCollection = serializeToVO(myObject, VOPerson); } ]]> </mx:Script> <mx:HTTPService id="myService" url="http://corlan.org/downloads/examples/get_data.html" method="POST" resultFormat="object" showBusyCursor="true" xmlDecode="serializeXMLToVO" result="resultListener(event)" fault="faultListener(event)"/> <mx:VBox left="25" top="25"> <mx:Button label="Get Data" click="myService.send()"/> <mx:Button label="Save Data Locally" click="saveData()" /> <mx:Button label="Clear data from server" click="myCollection = new ArrayCollection()"/> <mx:Button label="Read Data Saved Locally" click="readData()" /> <mx:DataGrid id="myGrid" dataProvider="{myCollection}"/> </mx:VBox> </mx:WindowedApplication>
I am curious if you run into the same issues and what solution did you find for.
What has been happening lately in the Adobe RIA space
So, what has been going on over the last few weeks in the Adobe RIA space? Actually a lot of things. And I think with all these developments, we will have a great MAX this year. And remember, there is much more news to come (remember Thermo and Gumbo, just to give an example).
LiveCycle Data Services ES 2.6
LCDS 2.6 version was released on 8th July. What’s new: improved server performance and scalability, alignment with Flex 3 and Flex Builder 3, Data Management improvements (paging, offline, subtype support, Hibernate support). You can read release information here.
Flex 4 SDK (code named “Gumbo”)
On 14th July Flex team published information about the next version of Flex 4 (code named “Gumbo”). As you probably already know, Gumbo has three main themes: Design in Mind, Developer Productivity and Framework Evolution. You can see a 14 minutes presentation on Gumbo here, or you could watch Ely Greenfield discuss developer/designer improvements, and here are more details on the themes. You can download nightly builds of the Flex SDK from here: http://opensource.adobe.com/wiki/display/flexsdk/Download+Flex+4.
Flash Player 10 beta 2
On 2nd July, the Flash Player 10 beta 2 was released on Adobe labs. It targets all three platforms: Windows, Mac and Linux and it has new features (custom filters and effects, 3D effects, new text engine, visual performance improvements, and enhanced sound API just to name some of them) and bug fixes.
Acrobat Reader 9
This version brings some interesting features: it has native Flash support (you can display SWF and FLV inside of an Acrobat document), it can use services from acrobat.com. With Acrobat Reader, you also get Adobe AIR (you need the runtime in order to use acrobat.com services – you can create PDF files online, edit with others, upload and share PDF). And of course it has better performance (improved launch time) and security (with support for 256-bit AES encryption and new digital signature functionality). And by the way, on 2nd July PDF became the latest ISO standard, you can read an article here on this subject.
Adobe Media Player 1.1
A new version of the AMP was launched last week. The welcome screen is redesigned and there are a lot of changes under the hood. I plan to do an interview this week with a couple of engineers from the AMP team, so stay tuned.
flex.org
flex.org has a new layout that should give a better user experience.
I guess the best way to see presentations and demos on all these products and on many more, is to come to Adobe MAX 2008 – either in San Francisco, in Milan, or in Tokyo :D.
Adobe MAX 2008 Europe – you can register
If you can’t go to the Adobe MAX 2008 San Francisco event, then you have another chance to attend Adobe’s biggest conference in this December 1st – 4th, in Milan (Adobe MAX 2008 Europe). For a limited period of time you can get 120 Euro off the regular registration price. Here is the site: http://max.adobe.com/eu/experience/. If you don’t know anything about this conference, just google it and you can find recordings and pictures from past year’s events.
Flex / AIR, PHP and user authentication
So you’ve built a Flex or AIR application that talks to a PHP server and you’re wondering how to authorize users? Actually, it is very easy to do this. You can do the same things you would do with a PHP web application: authenticate the user credentials, start a session and write some session values. Then, for any subsequent calls, first you check if the session values are set to determine if the user is logged in. This approach works in the same way for a Flex application deployed on the browser or deployed on the desktop (an AIR application), because once the server starts a session and sends the response back to the client, Flash player adds the session ID to any other call made to the same server (of course the calls must use HTTP or HTTPS).
So let’s recap what happens:
- The Flex client (could be a web app or an AIR app) makes a request to a PHP file to authenticate the user: http://localhost/login.php
- The “login.php” validates the user and if successful it starts a session and saves to the session some variables regarding the authenticated user
- The server sends a response back to the Flex client
- The Flex client makes new HTTP/HTTPS requests to the same server. The session id is appended to the request
- The PHP server scripts called from the client use the same session and they can retrieve from the session any info that was saved by “login.php“
Another way to handle the authentication is by using a token. The workflow is:
- The Flex client sends user credentials to the server
- If the user credentials are correct, the server sends back a token that is unique for the given user
- From that point on, whenever the Flex client makes a request to the server, it sends the token too.
Why should you use a token instead of relying on the session mechanism? I don’t think one method is always better than the other, it really depends on your architecture and your specific needs. For example if you use the token approach, you have two advantages: you don’t care about session expiration time and you don’t care about the server domain (for example you could have more domains sharing the same user database and using the token approach you don’t need to authenticate on each domain in order to start a session). On the other hand, using the session approach could be better if you can leverage existenting applications on the server side, or you don’t want the token appended to each request you make from Flex application.
I put together a simple Flex application to illustrate how you can authenticate from Flex on a PHP server. You can download the app from here and you can import the archive in Flex Builder using Import > Flex Builder > Flex project. Inside of the project is a folder “user_auth“. You should move this folder to your PHP web server and then you need to change, in the MXML file, the value of SERVER_URL constant. It should be the correct URL for your setup (it is defined right at top of the MXML file). On my computer, the constant has this value http://localhost/user_auth/. As a side note, this code works as well as an AIR application, just create an AIR project and copy the script code and the services/UI components into the MXML file and you are done.
The calls to the PHP server are made using either HTTPService or RemoteObject (I am using AMFPHP on the server side). If you run the application, you will see three areas with buttons: one for authentication using session and HTTPService, one for token authentication using HTTPService, and the last one for using RemoteObject and session. One way to test it is: open the the application in Firefox and click on the first button, a session will be started on the server and some values will be set in the session. If you open the same application in Safari (or any browser other than the one you just used) and click on the second button (Make Request to another page which…) you will get an error as the PHP script checks that some specific values are set in the session.
If you want to try the “Authenticate against LDAP” workflow, first you need to be able to access a LDAP server from the PHP server and you need to open the “ldap_auth.php” PHP file and set the correct values for RDN, DN, and hostname.
AIR: How to save locally a resource from Web
I am working on a very, very cool AIR project. I can’t say much about it for now, but in a couple of weeks I hope it will be released, and then I will blog about it. Anyway, while working on this project I had to implement different work flows revolving around online/offline status. One thing you often want to do in an AIR application is: save resources from the web to the local machine, either to make them available to the application even when a network connection isn’t available or just to optimize bandwidth usage.
That’s what this article is about. I will show you a simple snippet of code that just does the job. Here is the exported AIR project (download it, and you can import the archive using Import > Flex Builder > Flex Project) and here is the the application code:
<?xml version="1.0" encoding="utf-8"?> <mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="init()"> <mx:Script> <![CDATA[ import mx.controls.Alert; /** * loader object used to load content from web */ private var loader:URLLoader = new URLLoader(); /** * This function gets called when the application was loaded. * Initialize the loader object. */ private function init():void { //we want to download images and other binary resources, //so I overwrite the default value text to Binary loader.dataFormat = URLLoaderDataFormat.BINARY; //attach the event listeneri for load event and errors event loader.addEventListener(Event.COMPLETE, onLoad); loader.addEventListener(IOErrorEvent.IO_ERROR, onError); loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onError); } /** * This is the click handler for the button in page. * Just triger the load and disable the button so no other * request can be sent while it is already running one. */ private function saveResource():void { loader.load(new URLRequest(resURL.text)); btn.enabled = false; } /** * This function gets called when the resource was loaded. * We just extract the name of the resource and call the saveLocally() function * to write the object on the local machine. */ private function onLoad(event:Event):void { btn.enabled = true; //enable the button so we can send another request. var fileName:String = new String(resURL.text).split("/").pop(); //call the saveLocally function; loader.data has the bytes of the loaded resource saveLocally(fileName, loader.data); } /** * Error handler for loader object. * I just display the error message and enable the button for new requests. */ private function onError(event:ErrorEvent):void { btn.enabled = true; Alert.show(event.text, "Error"); } /** * This function does the actual saving. * @param file name to be used for saving * @param bytes to be written */ private function saveLocally(fileName:String, data:ByteArray):void { //create a file under the application storage directory using the filename argument var file:File = File.applicationStorageDirectory.resolvePath(fileName); if (file.exists) file.deleteFile(); //delete it if exists //create a file stream to be able to write the content of the file var fileStream:FileStream = new FileStream(); //open the file stream and set for Write fileStream.open(file, FileMode.WRITE); //writes the bytes fileStream.writeBytes(data, 0, data.length); //close the stream fileStream.close(); //display the path of the saved resources showLocalPath.text = file.nativePath; } ]]> </mx:Script> <mx:VBox width="820" x="10" height="200" y="10"> <mx:Label text="Save a web resource locally" fontSize="26"/> <mx:Label text="Enter a resource URL such as http://mysite/image.jpg" /> <mx:HBox> <mx:TextInput id="resURL" text="" width="600"/> <mx:Button id="btn" click="saveResource()" label="Save locally" /> </mx:HBox> <mx:Label id="showLocalPath" /> </mx:VBox> </mx:WindowedApplication>
Here is an explanation of what I did:
- To getting the resource from the web, I use URLLoader. This object has a property that I can set to control how the resource is loaded: dataFormat. Since I want to download binary objects such as images and movies, I set this property to URLLoaderDataFormat.BINARY. Next, I attach some functions to my loader for Complete and Error events.
- When the resource is loaded, the onLoad function gets called and I can use the data loaded to create a file locally. I have access to loaded data using the loader.data property. This property gives me a ByteArray.
- Having the data, I can write the file using the AIR API. I use File.applicationStorageDirectory.resolvePath(“file name”) to create a file in the Application storage folder. I write to this file using a FileStream.
- The rest of the code is just a simple UI: a text input to let the user to enter the URL for the resources, a button to execute the code, and a label that displays the path to the saved file.
Love is in the AIR
The Sun newspaper just launched, probably, the sexiest AIR application: Desktop Keeley! Yes, it is an RSS reader, but it is wrapped in some really nice clothes. Oops, actually not in too many clothes. Anyway, just go there and install it and come go back here and leave a message saying either you love it or not. The application was created by Glue London.
"Live Help / Call Center" application packaged using Flex Java WTP project and Export Flex Project Wizard
I am a little bit obsessed lately with the need to share a Flex project as easy as possible and to import a project with minimum work on my part. For example, I grabbed the LiveHelp / Call Center application made by Christophe Coenraets and I made it a single Flex with Java combined project with support for BlazeDS. What does this mean? It means if you have installed Flex Builder 3, WTP, and Apache Tomcat 5.5 you should be able to download this zip file, and then import the project using the Import > Flex > Import Flex project Wizard. You will have the BlazeDS server, Java code and Flex code in this project. After this, you’ll need to add this application to the Tomcat server. You can start the server from within Eclipse and run the mortgageapp.xml and callcenter.mxml and it should work like a charm.
However, because WTP links the name of the local JRE used in its configuration files, it is possible that the project will not work on your machine. The solution is very simple: just open the properties for the project and go to “Java Build Path”, then select the “Libraries” tab. Here you need to delete the “JRE System Library” entry and then click “Add Library” button. In the new window select the “JRE System Library” list item and click “Next”. On the next page select JRE 1.5 or newer and you are done.
My Ukrainian adventure
Come on guys, I am married man. So it is not about the beautiful Ukrainian women, it is about my last week’s trip to Ukraine for the Ukrainian and Russian FUG meeting. They organized some kind of summer camp in a remote location in Crimea peninsula, near Kerch town at Azov seaside, and I was lucky enough to get an invitation to present a session. I need to thank my boss, Enrique :)
The guys were simply amazing! I only wish that I could have stayed longer with them. The location was beautiful and the mix of good programmers, interesting projects and topics with the wild scenery, good beer and wine is a combination hard to beat! I had some time to talk with Rost, and the guys from AlternativaPlatform showed me their Flash 3D engine – awesome dudes! I am sure if I had more time to spend with them, I would have had the chance to get acquainted with everyone. Well, maybe next time. Guys I don’t want to hurt your feelings with this story, as I met you and other good people in my trip to Ukraine. But this is too funny not to tell :) So forgive me.
Flex SEO Contest
By now, probably you heard about the special player made by Adobe to help Search engines to index Flash applications. But, maybe you don’t know about this cool contest: Flex SEO Contetst by Ryan Stewart
LiveCycle Data Services ES Express and LiveCycle Data Services ES Single-CPU License
This spring at Flex360 in Milan, I presented a session about working with data in Flex Builder 3, and I was asked a lot about LCDS versus BlazeDS. When I asked questions about the use case, I found that most of the time the only reason to use BlazeDS instead of LCDS, was that BlazeDS is Open Source and thus free. Months later, I had a deja vu experience in Holland. It seems that very few people know that there used to be LiveCycle Data Services ES Express (for LCDS 2.5) and there is a LiveCycle Data Services ES Single-CPU License (for LCDS 2.51). This license, basically lets you use the full version of LCDS in production with this limitation: you have to deploy the LCDS on a machine with a single CPU (a multi-core CPU is counted as a single CPU) and a single application.
So, if you need the LCDS features and you can work within the license terms, then go and enjoy LCDS for free. I can think of a lot of applications built for small and medium intranets (CMS, mini ERPs and so on) that probably can be run successfully on a machine with a single CPU. And if the application grows over time, probably your client grew as well, and now they can afford to buy a license for multiple CPUs (they had the chance to test the technology and see its value gives and what problems it solves).
You can find more about the license here (click on “What happened to LiveCycle Data Services ES Express” for example) and this is a great post that explains the LCDS and BlazeDS products and licenses.






Android & AIR
PHP & Flex