Working with XML, E4X and ActionScript 3
by 12 March, 2009 7:20 pm17
On more than one occasion I have asked developers if they had experience with E4X. I am often surprised to find that some aren’t sure what this amazing ”thing” is and others have been using E4X without even realizing it!
So, what exactly is E4X? E4X is basically a Document Object Model (DOM) interface for XML. What does that mean in English? Well, if you use JavaScript and/or ActionScript you are using an ECMAScript laguage. ECMAScript languages are very similar in the way in which they handle access to their objects, those object’s children, properties and methods. Basically, if an item contains another item or property, you can access that child item or property along the lines of ParentItem.ChildItem. E4X provides us with the ability to do this with XML in ActionScript 3.
Requirements
Adobe Flash CS3
Source Files
E4X’s Addition to ActionScript 3
Prior to ActionScript 3 you had limited means for accessing nodes in the XML that you were using in your Flash applications. Many developers relied on third-party implementations of XPath to parse their XML when using ActionScript 2. Many developers also probably recall the long strings of ActionScript used to target specific nodes when not using XPath that looked something like: first.child.childNodes[0].childNodes[1].nodeValue. Even less efficient were the number of for loops that were used to parse through the XML nodes and arrays that were used to house them.
ActionScript 3 brought us a very strong implementation of E4X. This helps us do away with the old methods used for parsing XML. The XML Class has changed in ActionAcript 3, what used to contain the methods and properties you remember from the ActionScript 2 version of the XML Class is now the XMLDocument Class. When working with the ActionScript 3 version of the XML Class we will be using E4X to crawl through the data. Attempting to use the same methods from the ActionScript 2 version of the XML Class on its ActionScript 3 counterpart will meet you with undesired results.
Some Examples of ActionScript 2 and ActionScript 3 XML Parsing
I thought it would be helpful to show some examples of ActionScript 2 and ActionScript 3 parsing XML. Before we start, let’s look quickly at how we load XML in ActionScript 3 vs. ActionScript 2 (The files used in the following examples are available for download).
The XML We Will Be Loading (animals.xml)
<?xml version="1.0" encoding="UTF-8"?> <animals> <animal type="dog" name="Fido" age="2">Fido is a good dog.</animal> <animal type="dog" name="Ralph" age="1">Ralph is brown.</animal> <animal type="dog" name="Brian" age="1">Brian is Ralph's brother.</animal> <animal type="cat" name="Charlie" age="3">Charlie likes fish.</animal> <animal type="fish" name="Gulper" age="3">Gulper does not want to be eaten.</animal> </animals>
The XML above defines several animals of different types, each with different attributes assigned to them. Next, let’s look at methods for loading this XML into an ActionScript 2 and an ActionScript 3 file:
Loading XML in ActionScript 2
var xml:XML = new XML(); xml.ignoreWhite = true; xml.load("animals.xml"); xml.onLoad = onXMLLoaded; function onXMLLoaded():Void{ trace(xml); }
Loading XML in ActionScript 3
var xml:XML; var urlLoader = new URLLoader(); urlLoader.addEventListener(Event.COMPLETE,onXMLLoaded); urlLoader.load(new URLRequest("animals.xml")); function onXMLLoaded(e:Event):void{ xml = new XML(e.target.data); trace(xml); }
In the two examples above, we load our animals.xml file and then trace the contents of the XML to ensure that it has been loaded. Now that we have our XML loaded into memory, let’s look at several scenarios in which we parse the XML.
Parsing The XML
To begin, let’s look at a simple situation. Let’s retrieve the second animal in the XML data:
ActionScript 2
function getSecondAnimal():Void{ trace(xml.firstChild.childNodes[1]); }
ActionScript 3
function getSecondAnimal():void{ trace(xml..animal[1]); }
OK, this example seems simple enough at first glance. You will notice that the E4X version contains double dots (..). No, that’s not a mistake. The use of the .. allows us to basically look at the XML document as a whole and not worry about the schema of the XML.
In the example below, we nested the animal node for “Ralph” within a test node. The expression xml..animal would still be able to pick up “Ralph’s” node. This is invaluable when working with complex XML Schemas or when working with XML that may change frequently.
<?xml version="1.0" encoding="UTF-8"?> <animals> <animal type="dog" name="Fido" age="2">Fido is a good dog.</animal> <test> <animal type="dog" name="Ralph" age="1">Ralph is brown.</animal> </test> <animal type="dog" name="Brian" age="1">Brian is Ralph's brother.</animal> …
With ActionScript 2 on the other hand, we would require some slightly more sophisticated looping and conditional checking in order to process all of the nodes in the XML document. Though the use of the .. syntax is quicker for our own coding practices, it is probably more memory efficient and a better coding practice to be as direct as you can when writing your E4X expressions.
This ability of E4X alone is a major winner in my books! Let’s take a look at a more details example.
Looking for a Particular Type of Animal
Finding specific nodes in a very large XML file can be daunting. When it comes to E4X this becomes very simple. Let’s take a look at the differences between ActionScript 2 and ActionScript 3 for this scenario:
ActionScript 2
//get all animals 1 yr old function getOneYearOlds():Void{ var oneYearOldAnimals:Array = new Array(); //loop through the XML for(var i:Number=0;i<xml.firstChild.childNodes.length;i++){ if(xml.firstChild.childNodes[i].attributes.age == 1){ oneYearOldAnimals.push(xml.firstChild.childNodes[i]); } } trace(oneYearOldAnimals); }
ActionScript 3
//get all animals 1 yr old function getOneYearOlds():void{ var oneYearOldAnimals:XMLList = xml..animal.(@age == 1); //loop through the results for(var i:uint=0;i<oneYearOldAnimals.length();i++){ trace(oneYearOldAnimals[i].@name); } }
Ok, so at first glance it seems like a lot is going on here. The ActionScript 2 version above, which really could be written any number of ways, looks through the XML, finds nodes with the attribute of age equal to 1 and then pushes them to an Array. I used this as an example for simplicity’s sake. Imagine if there were other nodes with an age attribute equal to 1 that we were not interested in. What if there were animals nested in other nodes? Both of these cases would require extensive conditionals and loops to be written in order to ensure we gathered only the data we need. In the case above we are left with an Array containing only text as it has lost all of the XML properties it once possessed.
The ActionScript 3 version of this code introduces some new syntax. You will see that we loop through all of the animal nodes in the xml. We create a XMLList variable that holds all of the animal nodes where the age attribute is equal to 1. Notice in our for loop that we loop while i is less than oneYearAnimals.length(). length()is a method and not a property, forgetting this can drive you nuts while you wonder why your length is not being returned properly. We then loop through the XMLList and trace out the name attribute of each of the XMLList items. We are able to do this because they have maintained the XML data originally assigned to them.
Additional Functionality
When you think of your XML like you think about the DOM of Flash it becomes much easier to access the data you are looking for. In addition to simply returning nodes you can also crawl the XML much like you would nested MovieClips in Flash. For example, you can access the parent node of a returned node using parent() (once again, notice that this is a method call and not a property):
function getParent():void{ trace(xml..animal.(@age == 1).parent()); }
In the example above, we find the animals with an age equal to 1 and then request the parent of those nodes. Tracing out the above result would return the complete <animals> node and its children.
Another handy method is decendants()which will allow you to access every node below a given node in the flow of the XML. This function another valuable method of the E4X class as it will return more that just the direct children of a given node.
Writing to Your XML Using E4X
Another great feature of E4X is the ability to actually write to the XML quickly and easily. Keep in mind that when assigning values you will want to directly address a single node and not and XMLList. In the following example we will set “Ralph’s” animal type to “cat”.
//make Ralph a cat function makeACat():void{ var ralph:XML = xml..animal.(@type == "dog" && @name=="Ralph")[0]; ralph.@type = "cat"; trace(ralph.@type); }
As you can see, we can simply assign values to our XML and then code against them immediately!
Conclusion
This tutorial really only scratches the surface of what is possible using E4X. As you leverage this new functionality you will see what an invaluable piece of your ActionScript arsenal it will become.