3 Replies Latest reply on Mar 12, 2008 8:40 PM by bitwyse

    Creating custom class instances for XML nodes

    MrSmeeth
      Hi guys,

      I'm trying to load an external XML document in my application and create an instance of a custom class for each node in the XML based on the value of some of their elements. The instances created will eventually end up in a DataGrid by the way. The problem I'm having is there seems to be many ways of doing small parts of this and I have no idea how to make them all gel. Initially I'm using HTTPService to load the XML file but I've seen people just use an XML object. Then, after that, I initially set the loaded XML to an ArrayCollection but others have used XMLList or XMLListCollection. I've no idea what's the best way to do this.

      Eventually, when I've created all of these instances by looping over the XML and creating them how will I make them bindable to the data grid? I'm guessing I'll have to group them somehow...

      Any help would be greatly appreciated. Thanks
        • 1. Re: Creating custom class instances for XML nodes
          ntsiii Level 3
          * Create a custom class to be your dataGrid dataProvider "item" objects
          * Use HttpService to get the XML.
          * use an e4x expression to get the child nodes of the xml in an XMLList: myXML.children(), or myXML.myNodes;
          * use a for loop to loop over the XMLList
          * inside the loop, create a new instance of the custom class
          * From the myXMLList[ i ], get the all the needed attribute values and assign them to the appropriate properties in the custom class instance.
          * still inside the loop, use myArrayCollection.addItem(mycustomClass) to add the item to the dataProvider ArrayCollection.

          Tracy
          • 2. Re: Creating custom class instances for XML nodes
            MrSmeeth Level 1
            You're incredible, thanks very much!
            • 3. Re: Creating custom class instances for XML nodes
              bitwyse Level 1
              Hey Tracy,

              That is exactly what I was talking about in a previous post you replied to
              http://www.adobe.com/cfusion/webforums/forum/messageview.cfm?catid=585&threadid=1344350

              Anyhow, Below is some code I created to do what your saying somewhat dynamically. The idea being you can have many different object types that you may want to populate with data from XML. In my case I am using e4x as the result type from my web services. At present I have about 6 different classes that call this function.

              I'd love to get some opinions on the function. Good bad or ???? Any improvements etc????


              package . . . .
              {
              import flash.utils.describeType;
              import flash.utils.getDefinitionByName;
              import flash.utils.getQualifiedClassName;
              import mx.utils.ObjectUtil;

              /**
              * Utility class to convert xml based Objects to class instances.
              *
              * Takes a value object as the destination and an xmlList of data
              * Look through all the items in the value object. Note we are using classInfo..accessor since
              * our objects are bound all variables become getter / setter's or accessors.
              *
              * Also note, we can handle custom objects, arrays and arrayCollections.
              *
              * History
              * 03.11.2008 - Steven Rieger : Created class
              *
              */
              public final class XMLToInstance
              {
              public static function xmlToInstance( destinationObject : Object, sourceXMLList : XMLList ) : void
              {
              // Get the class definition in XML, from the passed in object ( introspection so to speak )
              var classInfo : XML = describeType( destinationObject );

              // Loop through each variable defined in the class.
              for each ( var aVar : XML in classInfo..accessor )
              {
              // If this is String, Number, etc. . . Just copy the data into the destination object.
              if( isSimple( aVar.@type ) )
              destinationObject[aVar.@name] = sourceXMLList[aVar.@name];
              else
              {
              // Dynamically create a class of the appropriate type
              var className : String = aVar.@type;
              var ObjectClass : Class = getDefinitionByName( className ) as Class;
              var newDestObject : Object = Object( new ObjectClass());

              // If this is a custom type
              if( isCustomType( className ) && ObjectClass != null )
              {
              // Recursively call itself passing in the custom data type and the data to store in it.
              // I haven't tested nested objects more than one level. I suppose it should work.
              // Note to self. Check.
              xmlToInstance( newDestObject, sourceXMLList[aVar.@name] );
              }
              else
              {
              // Must be some sort of Array, Array Collection . . .
              if( ObjectClass != null )
              {
              var anXMLList : XMLList = new XMLList( sourceXMLList[aVar.@name] );
              for each( var anItem : XML in anXMLList )
              {
              // I'm sure there are more types, just not using any of them yet.
              if( newDestObject is Array )
              newDestObject.push( anItem )
              else
              newDestObject.addItem( anItem );
              }
              }
              }
              // Add the data to the destination object. . . .
              destinationObject[aVar.@name] = newDestObject;
              }
              }
              } // end function objectToInstance


              public static function isSimple( dataType : String ) : Boolean
              {
              /**
              * This function is pretty self explanatory.
              *
              * Check to see if this is a simple data type. Did I miss any?
              *
              * History
              * 03.11.2008 - Steven Rieger : Created function
              *
              */

              switch( dataType.toLowerCase() )
              {
              case "number":
              case "string":
              case "boolean":
              {
              return true;
              }
              }
              return false;
              } // end isSimple

              public static function isCustomType( className : String ) : Boolean
              {
              /**
              * This function is pretty self explanatory.
              *
              * Check to see if this is a custom data type. Add them here as you need. . .
              *
              * History
              * 03.11.2008 - Steven Rieger : Created function
              *
              */

              var aClassName : String = className.replace( "::", "." ).toLowerCase();
              aClassName = aClassName.substr( aClassName.lastIndexOf( "." ) + 1, aClassName.length - aClassName.lastIndexOf( "." ) );

              switch( aClassName )
              {
              case "ndatetimevo":
              case "expenselineitemvo":
              {
              return true;
              }
              }
              return false;
              } // end isCustomType

              } // end class
              } // end package