Getting Advanced with E4X

Sometimes, when working with XML from webservices or public APIs, you will encounter certain elements or attributes in namespaces. For instance, the Yahoo! Weather RSS Feed has a yweather namespace that adds certain new elements to the feed that aren’t part of the regular RSS standard. To use E4X with complex XML like this, you’ll need to know a couple of things to make this namespaced data accessible.

For the following examples, I will be using the Yahoo! Weather feed for Sunnyvale, CA. It is available at the following URL:

http://weather.yahooapis.com/forecastrss?p=94089.

There are two ways to use namespaces in your E4X statements. You can reference the namespace directly in your query, or you can declare the namespace available for use globally. Regardless of which method you use, you’ll need to declare the namespace first. Notice that it is defined by a URL. I’ve taken this value directly from its declaration in the RSS feed itself.

namespace yweather = "http://xml.weather.yahoo.com/ns/rss/1.0";

First, let’s access the current temperature using the namespace inline:

var temp:int = rss.channel.item.yweather::condition.@temp;

It’s simple, but as you can probably imagine, a complex query will require too many references to the namespace. Alternatively, if you don’t want to litter your queries references to yweather::, you can tell the compiler to use this namespace globally.

use namespace yweather;

Now, we can write a simpler E4X query without the need to worry about our namespace:

var temp:int = rss.channel.item.condition.@temp;

Now, let’s look at the condition element in the feed a little bit differently. For debug purposes, let’s trace a few values to the console. Notice that we can get the string representation of the attribute’s name very easily through, you guessed it, the name() function.

var attributes:XMLList = rss.channel.item.condition.attributes();
var count:int = attributes.length();
for( var i:int = 0; i 

You can see that when you access an attribute from an element, you're actually getting a new XML object. When most people think of objects of this type, they expect to see an element, but it can hold many different forms of XML data. If you'd like to learn more, check out the documentation for the XML class, and its nodeKind() function in particular. You'll discover that an XML object can hold simple text, comments (<!-- comment -->), attributes, elements, and processing instructions.

Let's finish up with the sample XHTML document that I referenced in my second post about E4X.

var html:XML = <html>
<body>
	<ul class="links">
		<li><a href="http://www.yahoo.com/" class="josh">Yahoo!</a></li>
		<li><a href="http://www.adobe.com/">Adobe</a></li>
		<li><a href="http://joshblog.net/" class="josh">Josh's Blog</a></li>
	</ul>
	<div id="intro">
		<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>
	</div>
	<p>Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
</body>
</html>;

I recently discovered that if you try to reference an attribute that doesn't exist, you'll get a runtime error. For example, try to get the all the anchors (links) in the above document with a class named "josh". It doesn't work (without some extra massaging, as you'll see below) because the link to Adobe doesn't have a class attribute at all.

var anchors:XMLList = html.body..a.(@["class"] == "josh");
//error

What you can do to get around this problem is to use the hasOwnProperty() function available to all Objects. This will allow us to first check to see if the class attribute exists, and if it doesn't, it will skip that particular anchor thanks to the short-circuiting of the && operator.

var anchors:XMLList = html.body..a.(hasOwnProperty("@class") && @["class"] == "josh");

I expected that my first two posts about E4X (1, 2) would cover most of the important details. However, only a couple days after I finished the second one, I had already started remembering more features to explore and share. Will there be more? I don't have anything planned, but I won't be surprised if I discover a couple cool new techniques eventually. 🙂

About Josh Tynjala

Josh Tynjala is a frontend developer, open source contributor, bowler hat enthusiast, and karaoke addict. You might be familiar with his project, Feathers UI, an open source user interface library for Starling Framework that is included in the Adobe Gaming SDK.

Discussion

  1. Ben

    Another way to avoid errors with attributes that may not exist is to use this syntax:

    var anchors:XMLList = html.body..a.(attribute("class") == "josh");
  2. ryan

    i was working with some flickr feeds just the other day and had to use thier namespace… good post… e4x rules!!!

  3. Mark

    Theres also the block level directive ‘default xml namespace’

    if (feedXML.namespace("") != undefined){
        default xml namespace = feedXML.namespace("");
    }

    This sets your default namespace to the default of the xml your loading. I think it might be more effective then ‘use namespace’ as its got block level scope and is for xml namespaces in particular.

  4. Josh Tynjala

    Thanks, Mark. That’s an interesting one. Very unintuitive syntax, though.

    By “block level”, do you mean that it could be scoped to an if statement or any other block, unlike local variables which are always scoped to a function?

  5. Mark

    By block level, I meant function block like local variables, but looking at the docs:
    “The specified namespaces are removed from the set of open namespaces when the current code block is exited. ”

    it looks like ‘use namespace’ performs the same way

  6. Pingback: reintroducing.com Blogging Receptacle » Blog Archive » E4X Articles

  7. Roger Braunstein

    The other distinction of the highly mysterious default xml namespaceis that it also implicitly sets the namespace on new XML nodes you create. Woot!

  8. Gino Basso

    Great E4X blogs.

    One question: In the example to get all the anchors (links) with a class named “josh”, what if the class attribute was qualified? I’ve yet to come up with a solution.

    Also, with regards to namespaces, don’t forget you can use wildcards, as in:

    var temp:int = rss.channel.item.*::condition.@temp;
  9. Pingback: More XML filtering with E4X in ActionScript 3 - Josh Talks Flash

  10. Pingback: Ways to use E4X to filter data in ActionScript 3 - Josh Talks Flash