Achieving strong typing from web services in flex

Note: this is an article I wrote about a year ago for the CFDJ. It's a good article on how to get items from web services into flex as strongly typed objects. I'm posting it here too, just in case. here's the original: http://coldfusion.sys-con.com/read/264726.htm

Working with Web Services FLEX Class Introspection to gain strong typing

Recently, I worked on a project using FLEX to create a front end for java based web services where I ran into a favorite in the java world: Transfer Objects or Value Objects as they might also be called. Early on in the project I was a bit skeptical of the transfer object model but later on, after working with them for a while I came to respect the value that they offer.

Transfer objects allow two separate systems to trade descriptive data objects without fear that somewhere along the line the data will be mistyped or that the variable names will change. Transfer objects are an explicit agreement between all parties on how data will be encapsulated. They make integration easier because once everyone agrees on what the transfer objects should be, for the most part, everyone can separate and do their thing, knowing what variable names and types will be traded between the two (or more) services. And even in the case when variable names change, the compile process will catch variable name changes and alert the file locations to fix the problems.

Value objects are intrinsically supported when using remote objects, that is, class typing from a java class to a FLEX class carries over without any additional work in most cases. FLEX automatically does the work to transfer a class type when using remote objects (one of many reasons to use remote objects). However, when using SOAP web services strong typing is broken because the class associations are not automatically transferred to internal FLEX classes. It's unfortunate, and leaves a bit of a gap when trying to use strong typing and compile time error checking. What's the value of strong typing? It makes it easier to debug the application. Instead of complaining at some obscure point when a user clicks on certain button in the middle of a complicated workflow, it breaks during the compile process where the problem can be easily found and fixed. What could have been a 2 hour search for a misnamed variable becomes a 5 minute change.

The problem here though is how to get the untyped SOAP objects that are created by FLEX into typed objects that FLEX knows about – internal transfer objects. The values themselves are typed, but there is no association between the back end data transfer class and the FLEX data transfer class. Further, FLEX can't be told that X SOAP object is actually of type Y class. For example, trying to cast an incoming web service object into a user defined class will cause a runtime error.

public var dpHistory:HistoryTo = event.result.HistoryTo as HistoryTo;

or

public var dpHistory:HistoryTo = HistoryTo(event.result.HistoryTo);

When I started trying to use strong typing of classes on the FLEX side, I tried to accomplish the transition by writing a custom transfer function for each type of class that I had. For example,

var dpHistory:ArrayCollection = new ArrayCollection; var tempObj:HistoryTo; For (var i=0;i

What a pain! It's error prone and doesn't allow for reuse. It's boring and adds to the amount of upkeep you have to do if ever anything changes. Actually, in the beginning of using FLEX I avoided using transfer objects for the sheer reason that I didn't want to go through the pain to actually code up every one of my web service returns like this. There are a lot of benefits that are gained by using transfer objects, but at the time it sure looked like a lot of pain to go through to get it. It even appeared to me that the drawbacks (ie less code reuse, more duplicate coding, etc) outweighed the benefits. Sure, these functions can be written up and saved it off as an ActionScript file somewhere, they can be included in wherever needed, but it looked like one transfer function was needed per each transfer object. And that can really be a pain, especially in a large project.

However, following a few steps, the process becomes quite simple and dare I say it, easy. All that's needed is a generic object transfer utility. Once the transfer objects have been written on the FLEX side, then class introspection can be used to dynamically populate the values from untyped web service objects. Let me show you how I did it.

First off, create a transfer object class. One of the beautiful things about FLEX is automatic getters and setters for public variables on classes. This means that to create a transfer object (as long as nothing fancy is being done with it), can be very simple. FLEX is flexible enough to use explicit getters and setters as public properties or to take public properties/variables and create automatic getters and setters for them. Creating custom transfer objects then becomes a breeze and takes literally just a few minutes to put together.

For example a simple class might be a history display class:

package comp.myProject.to { public class HistoryTo { public var creationDate:String = new String; public var message:String = new String; public var objectName:String = new String; public var objectType:String = new String; public function HistoryTo() { } } }

Once the custom class (transfer object) has been created on the FLEX side, it can be used to store variables. Now FLEX Builder knows about this class and can display hinting on the class names, warn when a variable name has been misspelled, and make sure that the variables and values maintain their type across the application. The biggest problem is filling the class with the proper information. With a plain object or dynamic class, getting the dynamic values contained inside is done by simply looping through its properties. For example: for (item in myObject) { // do something here myVar = myObject[item]; }

Unfortunately, classes don't support this method for finding static properties (only dynamic properties). This is where class introspection comes to the rescue. Class introspection returns a listing of all static properties of a class and the types of those properties. Best of all, class introspection is actually very easy in FLEX. For example, there is a function in FLEX which returns an xml structure of all the class properties: import flash.utils.describeType;

var classInfo:XML = describeType(HistoryTo);

The function describeType returns an xml structure that can be looped over to get a listing of the public static properties, class methods, the class name, and the class base reference (if it extends a class). The function describeType will not return dynamic properties, private properties, or private functions. To get dynamic properties, loop through the class description. In the example, the classInfo variable now contains an xml object that can be looped through and/or treated like any other xml type data source. For instance, it could output the results in a tree (ala the old object inspector for FLEX 1.5). In this case, it can be used to loop through the FLEX side transfer classes, pull out the variables and types, check them against the web service objects, and transfer the web service object values to the appropriate class variables.

The root node, contains the name of the class and the name of the class that it extends (if any),

var className:String = classInfo.@name;

To get the list of public variables on the class is relatively simple as well.

classInfo..variable contains an array of variable names and variable types. Looping through those variables allows for assignment from one variable to the next:

for each (var v:XML in classInfo..variable) { //do something here }

With a list of the variables on the class, loop through and assign values to the class variables. Looping through the known classes allows the ability to look into the web service object to see if the variable is there. If the variable exists, then the value is automatically transferred over to the class. Because internal classes are static, an error is thrown if an attempt is made to set a variable that has not been written into the class at compile time.

if (translateFrom.hasOwnProperty(vName) && translateFrom[vName] != null) { // do something here }

From here, it's a matter of checking the type to transfer the variable accordingly. I've found that with some variable types (at least with my implementation) from java don't transfer over correctly. For example, longs sometimes don't transfer over as simple numbers and Booleans don't transfer over to a simple Boolean value. They sometimes transfer over as a complexString value type, which contains an xml snippet from the SOAP packet that FLEX received. In most cases when using the value in a complexString, it matches correctly and it is barely noticeable that the object isn't strictly the type needed. However, in certain cases, the difference becomes very noticeable. I've found that fixing it at the source helps to eliminate problems down the road.

case "Number" : translateTo[vName] = new Number(translateFrom[vName]); break;

This object translator can be extended to work with an array of objects as well. Once the class is inspected, you can then get the name of the class and use it to create a reference to that class object which opens up the ability to dynamically create new instances of that class.

var classRef:Class = Class(getDefinitionByName(className));

Then when looping through the class, create a new class object to store the data.

if (arrayOfObjects != null) { for (i=0;i

Notice that most of the functions within the class are static functions.

public static function translateObject(translateFrom:Object,translateTo:Object,classInfo:XML=null):Object {

Making the functions static functions means that the functions can be used without creating an instance of the class. Static functions are not tied to any particular instance of the class, but to all instances of that class. In effect it becomes like any other utility class that FLEX uses. An example of the difference between static functions and instance functions is best illustrated by the Date class. When using the date class, to store date information an instance has to be created, then functions are available to modify or get the information stored within that date object. For example: var myDate:Date = new Date; var myMonth:Number = myDate.getMonth;

The function getMonth is only available on an instance of the Date class. It uses information stored in that instance to return its value. The Date class also has a static function: Date.parse()

The parse function is a static function that's available on the class itself, not on a particular instance. Likewise, the once the object translator class is imported in the application file, any of it's functions can be used from the class itself.

import comp.ebay.utils.ObjectTranslator;

if (eventObj.result.HistoryTos != null ) { dpHistory.source = ObjectTranslator.translateArrayObjects(eventObj.result.HistoryTos.source,new HistoryTo);

}

For most cases, this returns what's needed in 90% of the cases; an array of objects. However there are times when all that's needed is a single object translated. In the function translateObject, it looks like an untyped object is being returned. In essence, that's only part of what's happening. This function returns a typed object under the guise of an untyped object. True, an untyped object is being returned, but now casting it as a specific class or transfer object will work because intrinsically it IS the needed class.

var historyItem = HistoryTo(ObjectTranslator.translateObject(event.result,new HistoryTo));

or

var historyItem = ObjectTranslator.translateObject(event.result,new HistoryTo) as HistoryTo;

Once all web service objects have been translated to internal classes, strong typing can be maintained by casting any web service objects to the transfer classes. If variable names on the transfer objects need to change (which often they do), it's a much smaller job to catch all of these problems at compile time than it is to debug and find them at run time.

When integrating with pre ColdFusion 7.02 web services where in most cases, all variable names are changed to upper case, you can easily add in a change to check for that occurrence, and easily translate it into the case you prefer internally in FLEX.

Here's the full code for the article. http://www.flexablecoder.com/blog/code/objectTranslator.zip

Comments (Comment Moderation is enabled. Your comment will not appear until approved.)
Doug Arthur's Gravatar I'm usinig Flex 3 beta 3, and this ObjectTranslator wasn't working for me.

It turns out that I had to change line 52:
old: for each (var v:XML in classInfo..variable) {
new: for each(var v:XML in classInfo.accessor) {

I just wanted to post this in case anyone else was having the same issue.

- Doug
# Posted By Doug Arthur | 2/18/08 10:50 AM
Jon Hirschi's Gravatar Doug, thanks for the info. I will update the object translator and put that in. I've also made some changes to way it handles sub objects, so that it can acutally handle them, so i'll add that in as well.
# Posted By Jon Hirschi | 2/18/08 1:13 PM
Jorge Villalobos's Gravatar Hello Jon,

First of all, thanks a lot for your code, it works great and it's been quite useful to me. Just a little comment on the sub class part (default case of the switch, line 138). Whenever I have a complex type which have another complex type as a member, I get a null value for that member. I don't really know if it's something that I'm doing wrong or what, but I've fixed it with the following:

Change this line:
translateTo[vName] = translateObject(translateFrom[fromName],translateTo[vName]);

with these:
var definition:Class = getDefinitionByName(vType.replace("::", ".")) as Class;
translateTo[vName] = translateObject(translateFrom[fromName], new definition());

I hope this helps in some way. Again, thanks for your work ;)
# Posted By Jorge Villalobos | 3/30/09 9:05 AM
BlogCFC was created by Raymond Camden. This blog is running version 5.7.