reflection based json validation with vo structs

a few weeks back, a friend of mine asked me “i have these json objects being returned from a java servlet. how do i validate that their contents are of the correct type and aren’t missing fields?”
this got me thinking about the very cool way in which remoting auto-wires inbound data to the correct vo class. that, in turn, got me thinking of how you could use a vo class as a struct to automatically validate data coming in from the server.
now when i say validate, i mean this:

1)   all properties on the vo are in the data from the server
2)   the data from the server matches the data types that the vo has defined

to really make all of this work, there are a couple of caveats… namely:

1)   all of the inbound messages from the server that are not translated to flash primitives have a corresponding actionscript classpath. (i believe this is the same way that remoting works under the hood – but its been a while since i’ve used it).
2)   also, this code would be more than worthless unless it could generically handle any kind of json -> vo translation. so we have to factor that in.

 

so here is a simple test to prototype the solution…

our json object:

{

“numberOfWheels”:4,

“nameOfDriver”:”jim bob”

}

and our corresponding vo:

package vo {

 

public class CarVO {

 

public var numberOfWheels:uint;

public var nameOfDriver:String;

 

}

}

 

now, here is our httpservice and the corresponding finish handler:

<mx:Script>

      <![CDATA[

           import vo.CarVO;

           import mx.rpc.events.ResultEvent;

           import mx.rpc.http.HTTPService;

           import factory.VOInstantiator;

           public function handleCreationComplete(evn:Event):void {

                  carService.send();

            }

            internal function handleJSONRetrieval(event:ResultEvent):void {

                  var test:CarVO = VOInstantiator.createVO(String(event.result)) as CarVO;

            }

      ]]>

</mx:Script>

<mx:HTTPService id=carService” 

      url=test_json/car.txt” 

      useProxy=”false” method=”GET” resultFormat=”text” 

      result=”handleJSONRetrieval(event)”>

</mx:HTTPService>

all pretty straight forward. the only interesting thing so far is this:
var test:CarVO = VOInstantiator.createVO(String(event.result)) as CarVO;

this is the guts of what this post is about. VOInstantiator.createVO builds a vo against the definition stored in a json struct. for that to happen, though, the struct has to have a property which is consistent across all json objects which will be funneled through this process. for my demo, i have chosen to use “clientClassPath” as this key. With that in mind, our json object is going to look more like this:

{

“clientClassPath”:”vo.CarVO”,

“numberOfWheels”:4,

“nameOfDriver”:”jim bob”

}

Now here is the util class that does all of the work:

package factory {

 

      import com.adobe.serialization.json.JSON;

      import flash.utils.describeType;

      import flash.utils.getDefinitionByName;

 

      public class VOInstantiator {

 

            public static function createVO(jsonSource:String):Object {

                  var returnObject:Object = mapToFlexObjects(decodeJSONObject(jsonSource));

            return returnObject;

            }

 

            internal static function mapToFlexObjects(obj:Object):Object {

                  var objClass:Class = getDefinitionByName(obj["clientClassPath"]) as Class;

                  var returnObject:Object = new(objClass)();

                  var propertyMap:XML = describeType(returnObject);

                  var propertyTypeClass:Class;

 

                  for each (var property:XML in propertyMap.variable) {

                        if ((obj as Object).hasOwnProperty(property.@name)) {

                              propertyTypeClass = getDefinitionByName(property.@type) as Class;

                              if (obj[property.@name] is (propertyTypeClass)) {

                                    returnObject[property.@name] = obj[property.@name];

                              }

                        }

                  }

 

                  return returnObject;

            }

 

            internal static function decodeJSONObject(jsonSource:String):Object {

                  var jsonObj:Object = JSON.decode(jsonSource);

                  return jsonObj;

            }

 

      }

}

The brunt of the work is being done in the mapToFlexObjects(obj:Object) method.
The first thing we do is create a class definition based on the class path that was in the json struct. this is done with the following code:

var objClass:Class = getDefinitionByName(obj["clientClassPath"]) as Class;

var returnObject:Object = new(objClass)();

then, we get an xml definition of that class via describeType. I absolutely love this api. here is the output for our CarVO:

<type name=”vo::CarVO” base=”Object” isDynamic=”false” isFinal=”false” isStatic=”false”>

  <extendsClass type=”Object”/>

  <variable name=”numberOfWheels” type=”uint”/>

  <variable name=”nameOfDriver” type=”String”/>

</type>

this really gives us the meat of how we can validate our json struct. the for loop iterates over each variable name in the vo and sees if a matching property name exists in the json object. if it does, it then checks to ensure that the data at that named index in the json object is of the correct type that the vo is inspecting. This type checking is accomplished by instantiating the class reference in the ‘type’ attribute in the above xml documentation. Then we do a simple check to see if the data being tested in the json object is of the same type as the new class. this is especially powerful for cases like int vs uint.

finally, if all of these cases are met, then it copies the data out of the json object and puts into the vo. when all is said and done, we have a typed vo object instantiated and populated that we can send back to the ui or whatever else is waiting for it.

obviously, there’s quite a bit of flexibility in terms of error handling/checking here. ultimately, i think this could easily be adapted to do the following:

1)   be a central point to funnel all json requests through. in pureMVC, it would be quite easy to automatically dispatch load notifications based on what kind of object is returned from here.
2)   get a base level of error checking and redundancy on data coming into the app. data leaving the app is easy to control.. data coming into the app is harder.
3)   refactor to be recursive in nature to handle more complicated nested structures… say, a vo containing vo’s… 

but really, i think the best part about this is that its ultra generic! it doesnt need to be built to handle specific classes… as long as it can generate a class that is fed to it in the json object, it can handle it. of course, you could run into issues with the flash JIT compiler here with class linkages, but theres innumerable workarounds for that. ill go over that in a separate blog.

 

anyways, thats all for now. i hope that helps somebody out.

About these ads

10 responses to “reflection based json validation with vo structs

  1. wowza. Nice coding. Question: how could I make this with arrays of VOs? Is there anyway to avoid looping and creation of duplicate objects? This is a more generic solution, but how would it compare to duping the hierarchy and creating new objects? Thanks for posting.

    • hi there Doug -
      you would need to differentiate between flash primitives (String, Boolean, int, uint, etc) and complex datatypes (Array, ArrayCollection, your own classes). then you would need to separate Flex and Flash complex classes (like Array and ArrayCollection) from your own classes. then recurse over those objects and populate their data fields just like you would with a singular instance above.
      it would get complex fast but definitely doable :)

  2. Hi,

    brilliant work! On our project we are slaved to use json instead of nice remoting. How is the progress on handling recursive structures?

    User hasMany buildings (ArrayCollection)

    Cheers!

    Florian

    • hi there florian -
      unfortunately i have not had the time to devote to fully flushing out this solution into a stand alone library. its still on my list of things to do though!
      thanks :)

  3. (I’m new to ActionScript, coming from a java background)

    I have an existing java servlet that returns a complex JSON result that I now need to work with in Flash. One of the fields is a date, where it is represented in milliseconds since epoch (long). In JS, this is directly converted into a Date. If I create a VO in ActionScript to represent this object in the result, is it possible to automatically convert the long value into a Date, or do I need to test for specific field names and then use the Date constructor with 1 param?

    Also, what is the benefit to creating a specific VO instead of using a generic object if the VO doesn’t have any actual functionality?

  4. Just an fyi – I needed to change this line in the code:

    for each (var property:XML in propertyMap.variable)

    to

    for each (var property:XML in propertyMap.accessor)

    to get it to work when using public getter functions for the attributes

    Thanks!

  5. Wonderful, thank you.

    BTW – The object I’m serializing contains other custom objects so I added this:
    else
    {
    returnObject[property.@name] = mapToFlexObjects(obj[property.@name]);
    }

    after this bit in the mapToFlexObjects function
    if (obj[property.@name] is (propertyTypeClass))
    {
    returnObject[property.@name] = obj[property.@name];
    }

    Works for me, but I realise it’d probably break the generic nature so it should be done slightly differently – just adding it here in case it’s handy for anyone else.

    Cheers
    Chris

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s