Integrating Flex with XML using XMLConverter

XMLConverter can be downloaded here
XMLConverter source can be downloaded here

Introduction

XMLConverter is an Actionscript library that converts Value Objects to XML, and vice-versa. It uses a combination of reflection, converters, and hints in your code to produce the XML you need for your back-end applications. It was written after having good experiences with XStream, whilst writing Java backends for our Flex applications, and not being able to find a suitable equivalent in Actionscript. You will notice that some concepts from XStream have been used in XMLConverter.

Usage

Using XMLConverter is very simple. Just call toXML() and fromXML() to do the conversion! Below we step through an example of a simple Value Object and how XMLConverter can be used to marshal it to XML and back.

Example

Let's pretend that we're writing an application that will send a customer order to a server, via XML. For now, our Order Value Object only stores simple attributes, as can be seen below:

package net.customware.flex.vo {

    public class Order{
        
        public var id:int;
        
        public var orderDate:Date;
        
        public var subTotal:Number;
        
        public var tax:Number;
        
        public var total:Number;
        
    }
}

Once the application has a populated Order ready to send, all you need to do is tell XMLConverter to convert it.

var order:Order = new Order();
                
order.id = 1234;
order.orderDate = new Date();
order.subTotal = 9.95;
order.tax = 0.99;
order.total = 10.94;

var orderXML:XML = XMLConverter.getInstance().toXML( order );

The resulting XML would then be something like:

<net.customware.flex.vo_Order>
  <orderDate>11/01/2008</orderDate>
  <total>10.94</total>
  <subTotal>9.95</subTotal>
  <id>1234</id>
  <tax>0.99</tax>
</net.customware.flex.vo_Order>

What XMLConverter has done here is use reflection to gather information about the object, and write it accordingly to XML. XMLConverter works with public variables (whether they're bindable or not), Actionscript set/get methods, as well as Java style set/get methods (eg: setId(int id) and getID() ).

You'll notice that the root element of the XML is the fully qualified class name, with some tweaks to make it XML friendly. This can be changed though, by using an alias.

Aliases

Aliases can be used in XMLConverter to replace the class names in the XML with user-friendly names. To register an alias with XMLConverter, simply call the registerAlias method, passing in the class you wish to alias, and the name you want to use.

    // register all aliases prior to calling toXML() or fromXML()
    XMLConverter.getInstance().registerAlias("order", Order );

By adding the one line of code to the previous example, the new XML would look like this:

<order>
  <tax>0.99</tax>
  <total>10.94</total>
  <orderDate>11/01/2008</orderDate>
  <id>1234</id>
  <subTotal>9.95</subTotal>
</order>

The order in which the elements are written depends on the Actionscript Reflection API. If you would like to have your XML with the elements in a specific order, you need to have your class implement the XMLElementOrderable interface.

Ordering the elements

XMLConverter has a feature that allows you to tell it which order the properties of your value objects are to be converted to XML. This is done by implementing the XMLElementOrderable interface, which can be seen below:

package net.customware.flex.util.xml.converter {
    public interface XMLElementOrderable {

        function getElementOrder():Array;

    }
}

Here's an example of an implementation, in our Order object:

package net.customware.flex.vo {

    import net.customware.flex.util.xml.converter.XMLElementOrderable;
    import mx.rpc.events.AbstractEvent;
    
    public class Order implements XMLElementOrderable {
        
        public var id:int;
        
        public var orderDate:Date;
        
        public var subTotal:Number;
        
        public var tax:Number;
        
        public var total:Number;
        
        public function getElementOrder(): Array {
            return ["id","orderDate","subTotal","tax","total"];
        }

    }
}

As you can see, we're telling XMLConverter how we want the XML elements to be ordered. By adding the getElementOrder() method to our class, the resulting XML would now look like:

<order>
  <id>1234</id>
  <orderDate>11/01/2008</orderDate>
  <subTotal>9.95</subTotal>
  <tax>0.99</tax>
  <total>10.94</total>
</order>

Now let's assume that we need to make the id an attribute in the XML. The way to tell XMLConverter how to convert a variable to an attribute is to prefix the variable name in the getElementOrder() method with the @ character. Example:

// snip
public function getElementOrder(): Array {
    return ["@id","orderDate","subTotal","tax","total"];
}
// snip

Would result in the XML looking like:

<order id="1234">
  <orderDate>11/01/2008</orderDate>
  <subTotal>9.95</subTotal>
  <tax>0.99</tax>
  <total>10.94</total>
</order>

Converters

You may have noticed that the orderDate attribute has been nicely written to the XML using the DD/MM/YYYY format. XMLConverter knows how to convert specific classes because XMLConverter by default has a DateConverter registered with it. If you have a value object that needs special attention when converting, create a class that implements the Converter interface, and register it with XMLConverter (by using registerConverter(). Also note that the default DateConverter can be overwritten with your own. This handy if the format of your dates is different to the default XMLConverter format. To override the format, simply register the XMLConverter DateConverter, but give it the new format. Example:

XMLConverter.getInstance().registerConverter(Date, new DateConverter("YYYY-MM-DD") );

This will produce the following XML:

<order id="1234">
  <orderDate>2008-01-11</orderDate>
  <subTotal>9.95</subTotal>
  <tax>0.99</tax>
  <total>10.94</total>
</order>

Lists and complex Value Objects

Let's make the Order class a little more descriptive, by adding order lines to it.

//snip Order.as
public var orderLines:ArrayCollection;
//...
public function getElementOrder(): Array {
    return ["@id","orderDate","subTotal","tax","total","orderLines"];
}
//snip
// this has a variety of variable types (Bindable, set / get methods, as well as Java-style setXxx and getXxx methods).
package net.customware.flex.vo {
    import net.customware.flex.util.xml.converter.XMLElementOrderable;
    
    public class OrderLine implements XMLElementOrderable {
        
        public var orderLine:int;
        
        [Bindable]
        public var quantity:int;

        private var _productId:String;
        
        private var _unitPrice:Number;
        
        public function set productId( productId:String ): void {
            _productId = productId;            
        }
        
        public function get productId(): String {
            return _productId;
        }
        
        public function setUnitPrice(unitPrice:Number): void {
            _unitPrice = unitPrice;
        }
        
        public function getUnitPrice(): Number {
            return _unitPrice;
        }
        
        public function getElementOrder(): Array {
            return ["@orderLine","productId","quantity","unitPrice"];
        }
        
    }
}

Now let's populate the order with a couple lines.

                var order:Order = new Order();
                
                order.id = 1234;
                order.orderDate = new Date();
                order.subTotal = 9.95;
                order.tax = 0.99;
                order.total = 10.94;
                order.orderLines = new ArrayCollection();
                
                var tempOrderLine:OrderLine = new OrderLine();
                tempOrderLine.orderLine = 1;
                tempOrderLine.productId = "ABC";
                tempOrderLine.quantity = 1;
                tempOrderLine.setUnitPrice(5);
                
                order.orderLines.addItem(tempOrderLine);
                
                
                tempOrderLine = new OrderLine();
                tempOrderLine.orderLine = 2;
                tempOrderLine.productId = "DEF";
                tempOrderLine.quantity = 1;
                tempOrderLine.setUnitPrice(4.95);

                order.orderLines.addItem(tempOrderLine);
                
                
                XMLConverter.getInstance().registerAlias("order", Order );
                XMLConverter.getInstance().registerAlias("orderLine", OrderLine );
                XMLConverter.getInstance().registerConverter(Date, new DateConverter("YYYY-MM-DD") );
                
                var orderXML:XML = XMLConverter.getInstance().toXML( order );

This would produce the following XML:

<order id="1234">
  <orderDate>2008-01-11</orderDate>
  <subTotal>9.95</subTotal>
  <tax>0.99</tax>
  <total>10.94</total>
  <orderLines>
    <orderLine orderLine="1">
      <productId>ABC</productId>
      <quantity>1</quantity>
      <unitPrice>5</unitPrice>
    </orderLine>
    <orderLine orderLine="2">
      <productId>DEF</productId>
      <quantity>1</quantity>
      <unitPrice>4.95</unitPrice>
    </orderLine>
  </orderLines>
</order>
Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.
  1. Jan 28, 2009

    Sandeep Malik says:

    Hi Daren, Greetings! I had some need of XMLConverter in one of my PoCs. Howeve...

    Hi Daren,

    Greetings!

    I had some need of XMLConverter in one of my PoCs. However, somehow its breaking at some object types. Can you please let me know or give me the source code of what you have developed so far and I can see and do my modifications.

    Regards,
    Sandeep

    1. Feb 19, 2009

      Alwyn Wong says:

      Source code is now available here

      Source code is now available here

  2. Feb 18, 2009

    Wolmar Machado says:

    Where is the source code ?

    Where is the source code ?

    1. Feb 19, 2009

      Alwyn Wong says:

      Hi Wolmar, I have attached the source code to this page. Alwyn

      Hi Wolmar,

      I have attached the source code to this page.

      Alwyn

  3. Mar 02, 2009

    Ali Ebrahimzadeh Tari says:

    I want to add some ArrayCollection to the root of an object. is there any way to...

    I want to add some ArrayCollection to the root of an object. is there any way to do that? for example:

     <order id="1234">
      <orderDate>2008-01-11</orderDate>
      <subTotal>9.95</subTotal>
      <tax>0.99</tax>
      <total>10.94</total>
      <!-- I've deleted OrderLines tag -->
      <orderLine orderLine="1">
          <productId>ABC</productId>
          <quantity>1</quantity>
          <unitPrice>5</unitPrice>
      </orderLine>
      <orderLine orderLine="2">
          <productId>DEF</productId>
          <quantity>1</quantity>
          <unitPrice>4.95</unitPrice>
      </orderLine>
      <!-- I've deleted OrderLines tag -->
    </order>
    
  4. Apr 22, 2009

    Steve Mosley says:

    Must say, very cool tool ... I really like xStream in the Java world for this ty...

    Must say, very cool tool ... I really like xStream in the Java world for this type of job... and I like the way this works like xStream

    I have noticed though that the swc download is ~300Kb while the source is only ~36Kb .... are some files being accidentaly added into the swc?

    I'd also say that it would be cool to do what Ali is asking for in the previous comment ... doesn't matter much for me but would make the xml look a bit cleaner.

    Thanks
    Steve

  5. Jul 09

    Tom Brooks says:

    We were able to get your toXML()call to function correctly and it works nicely, ...

    We were able to get your toXML()call to function correctly and it works nicely, however we are having some problems getting the fromXML() to execute properly. Can you please provide a quick example of how is this suppose to be used? I didn't see any reference to its use in your documentation above.

    After pulling in the source and debugging, it appears that it only loops through the top level node once and checks if it has any attributes then returns the result. I thought it was a recursive loop that was suppose to loop through all child nodes and then kick back the resulting object?

    1. Jul 10

      Alwyn Wong says:

      Hi Tom, I created a quick example in how you can use the XMLConverter to conver...

      Hi Tom,

      I created a quick example in how you can use the XMLConverter to convert Value Objects to XML, and vice-versa.

      Here is a link to the flex application:

      Click here for the source code to the above example.

      Here is the snippet that you might be interested in which covers how to convert XML back to VO.

      // snip	
      /* This method is responsible for converting XML back to VOs */
      protected function addToDatagrid(event:MouseEvent):void {
          // get your String representation of the XML to want to parse - in this example it is from a textarea
          var myXML:String = xmlTextArea.text;				
          try {		
              // contruct XML object because that is what the converter expects
              var xml:XML = new XML(myXML);
              // throw the XML obejct to the converter 
              var orderFromXml:Order = XMLConverter.getInstance().fromXML(xml) as Order; // we are expecting a Order VO from the converter
              //  debuging - lets validate some of its properties
              trace(orderFromXml.id);
              trace(orderFromXml.orderLines.length);
              trace(orderFromXml.orderDate); // etc etc	
              // add it to the list to be displayed in the datagrid
              orders.addItem(orderFromXml);
              // display success message
              errorlabel.text = &quot;Converted successfully...&quot;;
          } catch (err:Error) {
              // handle your bad XML excecptions etc etc here...
              trace(err);	
              errorlabel.text = err.message;
          }
      }
      // snip			
      

      The converter should recursively loop through all the child nodes - see the OrderLine value object.