E4X tip: writing expressions to test attributes that may not exist

E4X is an awesome way to work with XML. But one common pitfall is trying to write an expression that tests the value of a particular attribute, when some of the XML nodes may not even have that attribute.

For example, let's say you have this XML in a variable called albums:

var albums:XML =
<albums>
  <album name="The Dark Side of the Moon" artist="Pink Floyd" year="1973" />
  <album name="Rubber Soul" artist="The Beatles" year="1965" />
</albums>

It's easy to write an expression to get all albums that were released in the 1970s:

var seventiesAlbums:XMLList = albums.album.(@year>= 1970 && @year <= 1979);

But let's say some of the XML nodes you are testing don't have a "year" attribute:

var albums:XML =
<albums>
  <album name="The Dark Side of the Moon" artist="Pink Floyd" year="1973" />
  <album name="Rubber Soul" artist="The Beatles" year="1965" />
  <album name="Haunted" artist="Poe" />
</albums>

In that case, unfortunately the conditional-test part of the above E4X expression, (@year >= 1970 && @year <= 1979), will throw an exception:

ReferenceError: Error #1065: Variable @year is not defined.

So, what to do? I've seen various complicated solutions to this problem; but to me the cleanest one is to replace @year with attribute("year") -- do this for any attribute that you don't know for sure will be present:

var seventiesAlbums:XMLList = albums.album.(attribute("year")>= 1970 && attribute("year") <= 1979);

True, this is more verbose and slightly uglier; but it's not too bad.

The reason this works is that the function XML.attribute() is documented as returning an empty XMLList when the attribute you request does not exist. But when you just say @year, that's like saying "trace(blah)" where blah is some variable that does not exist -- the player tries to find something called @year, and it can't, so it complains.

9 Comments so far

  1. Marcus on November 2nd, 2006

    Thanks Mike.
    Handy tip. And its not really that ugly…

  2. Alex on January 27th, 2007

    if (selectedNode.hasOwnProperty("@link") == true) {

  3. mike on January 28th, 2007

    Yes, selectedNode.hasOwnProperty() can be used to tell whether an attribute is present on a node, which is sufficient in some cases; but usually, you want to test for certain values. For example, if you want to select only nodes that have an "x" attribute equal to "y", then using hasOwnProperty, you would have to write

    hasOwnProperty("@x") && @x == "y"

    whereas if you use the attribute() function, it’s cleaner:

    attribute("x") == "y"

  4. William G on October 16th, 2007

    Thanks!!! Your post saved me some hours !
    I don’t know if it’s only me but the Flex doc is sometimes hard to understand…especially the XML/XMLList part…

  5. Rick T on January 2nd, 2008

    A great suggestion. I hope you don’t mind that I mentioned your blog on the Adobe site in response to a query about this topic.

    BTW, do you have any suggestions for the best technique for testing for existence of a node?

  6. mike on January 2nd, 2008

    Rick, use the elements() function, and test that the XMLList returned by the elements() function has a length of 1 or greater. For example, to return a list of nodes of type "child", but only those ones that contain another element called "grandchild":

    var myxmlvar:XML =
    <root>
    <thing1 id="1"/>
    <thing1 id="2"> <thing2/> </thing1>
    </root>;
    var mylist:XMLList = myxmlvar.thing1.(elements("thing2").length() > 0);

    To learn more about functions such as attribute() and elements(), see the documentation for class XML ( http://livedocs.adobe.com/flex/201/langref/XML.html ) and class XMLList ( http://livedocs.adobe.com/flex/201/langref/XMLList.html ).

  7. Oleg on January 17th, 2008

    Thanks. This trick saved me time.

  8. Jesse Freeman on August 28th, 2008

    Thanks for this post, for the past 30 mins I thought I was going nuts. This did the trick, keep up the good work!

  9. Bart vbl on July 5th, 2010

    Thanks for saving me a LOT of time which I’d have spent figuring this out myself ^^

Leave a reply

*
To prove you're a person (not a spam script), type the security word shown in the picture. Click on the picture to hear an audio file of the word.
Click to hear an audio file of the anti-spam word