Flash E4X Tutorial

This is a beginner's guide to E4X.
Flash has some good documentation on using E4X at:
http://livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/index.html?XML.html&class-list.html,
but it's hard to digest all that if you're just getting started.

You can download the example attached below and follow along.


Loading xml

Before we begin parsing any xml, I'll explain briefly how to load the xml.
The first way to load xml is to simply have the xml right inline with your code. In Flash 9 you can do this just simply by using the following syntax:

var myXml:XML = 
	<data>
		<user/>
		<user/>
	</data>;

Flash knows when the xml is done based on the semi-colon at the end, so don't forget that.
The other way to load xml is to use a URLLoader.
package {
	import flash.events.SecurityErrorEvent;	
	import flash.events.IOErrorEvent;	
	import flash.events.Event;
	import flash.net.URLLoader;
	import flash.net.URLRequest;		
 
	public class LoadFileExample {
		function LoadFileExample() {
			var urlLoader:URLLoader = new URLLoader();
			var urlRequest:URLRequest = new URLRequest();
			urlRequest.url = "users.xml";
			/* 
			 * Important to listen for IOError events and SecurityErrorEvents because otherwise 
			 * Flash throws an unhandled exception
			 */
			urlLoader.addEventListener(Event.COMPLETE, completeHandler);
			urlLoader.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
			urlLoader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
			urlLoader.load(urlRequest);
		}
		private function completeHandler(event:Event):void {
			var urlLoader:URLLoader = URLLoader(event.currentTarget);
			var xml:XML;
			try {
				// urlLoader.data is the raw string containing the xml from the file.
				xml = new XML(urlLoader.data);
			} catch (err:Error) {
				trace("There was a problem parsing the xml.");
				return;	
			}
			// Do the xml parsing here.
 
		}
		private function ioErrorHandler(event:IOErrorEvent):void {
			trace("Could not find users.xml");
		}
		private function securityErrorHandler(event:SecurityErrorEvent):void {
			trace("There was a security error loading users.xml");
		}
	}
}

When you load XML through a URLLoader, there are a few key points to pay attention to.

  • Make sure to listen for IO_ERROR and SECURITY_ERROR events on the URLLoader object.
  • Place a try/catch around your instantiation of the XML object.


Parsing xml

Let's do three basic tasks with xml.

  • Iterate over the users.
  • Find a user with a given id.
  • Find all users that weigh over 300 lbs.

The first thing important to understand is the difference between the classes XML and XMLList. An XML instance is just an object that has E4X parsing abilities. If you say new XML(xml_str); You are instantiating an XML object with the raw xml String and creating a flash object that is automatically ready to traverse with E4X. An XMLList is like an Array of XML objects, except that if there is only a single element, you can treat it just like an XML object.

Examples are based on using the following xml:

var xml:XML = 
<data>
	<user user_id="1" group_name="friends">
		<name>Surly Craigsworth</name>
		<country>USA</country>
		<weight>400</weight>
	</user>
	<user user_id="2" group_name="work">
		<name>Nordan Jordan</name>
		<country>Canada</country>
		<weight>90</weight>
	</user>
	<user user_id="3" group_name="friends">
		<name>Jophas Morian</name>
		<country>Italy</country>
		<weight>150</weight>
	</user>
	<user user_id="4" group_name="work">
		<name>Garth Riddleberg</name>
		<country>USA</country>
		<weight>450</weight>
	</user>
</data>;

Iterating over an XMLList.

We can treat the XML object like a regular flash object, so if we want to get the first user, we can simply use:

var userXml:XML = xml.user[0];

xml.user is an XMLList, and xml.user[0] is a single XML object.
There are two ways to iterate over this XMLList.
Method 1:
var numUsers:uint = xml.user.length();
for (var i:uint = 0; i < numUsers; i++) {
  var iUser:XML = xml.user[i];
  trace(iUser.@user_id + " " + iUser.name);
}

Note that unlike with an Array, it's length(), not length.
Also notice the iUser.@user_id -- this can also be: iUser.attribute("user_id"); For keywords like id or class, you always need to use the latter.
2nd approach:
for each (var iUser:XML in xml.user) {
  trace(iUser.@user_id + " " + iUser.name);
}

Same thing, just a little cleaner.

Finding a user with a given id.

In E4X, there is a nice way of having selectors basically do queries in the xml for you. Here's getting the user with id 3:

  var user3:XMLList = xml.user.(@user_id == 3);

If you give any XMLList a condition like that, it will return an XMLList that satisfies that condition. Since we know there's only one user with id 3, we can cast it to an XML object without problems.

Find all users that weigh over 300 lbs.

Another example using the conditional selector:

  var bigUsers:XMLList = xml.user.(weight > 300);

We can pretty much have any condition after an XMLList object and that will give us the selection we want. This returns all XML nodes in the xml.user XMLList that satisfied that condition. If you have several branches within your XML and want to get all users over 300 lbs, you can use the descendants property.
  var bigUsers:XMLList = xml.descendants().user.(weight > 300);

If your xml structure was like the following:
  data
     group
         user  (weight 600)
         user  (weight 50)
     group
         user (weight 500)
         user (weight 900)

That code will return an XMLList of all 3 of those users that have a weight of over 300.

AttachmentSize
XMLParsingExample.zip550.33 KB

I've read better. :P Especially by that judah guy. come to bbt!!!

Really nice! Thanks!

I have an odd situation compared to the above:
An 'item' with multiple 'category' children.
If I use the xml..item(category=='blue') syntax, only the items that have exactly ONLY blue as a category are returned.
The ones that are also in the category 'square' are not returned.

do you have an idea how to deal with it?

regards,
Bart

Hi, This is Really Helpful.

var user3 : XML = xml.user.(@user_id == 3);
trace(user3.@user_id);

Exception fault: TypeError: Error #1034: Type Coercion failed: cannot convert XMLList@27276071 to XML.

and if i do this..

var user3 : XML = xml.user.(@user_id == 3) as XML;
trace(user3.@user_id);

we get...

Exception fault: TypeError: Error #1009: Cannot access a property or method of a null object reference.

any ideas?

Yes, when you do an e4x expression, it will always return an XMLList, not an XML object. Think of it like you're doing a search for something and it's giving you the results.

So instead of:
var user3 : XML = xml.user.(@user_id == 3) as XML;

Do:
var usersWithId3 : XMLList = xml.user.(@user_id == 3) as XML;
trace(usersWithId3[0].@user_id);

Your explanation for the XML vs. XMLList object use-case was very concise and helpful - many thanks! You should write a book. :- )

:) considering I have been working with e4x for 2+ weeks :) this is one of the best links I have bumped into...