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.
Comments(9)
Thanks Mike.
Handy tip. And its not really that ugly…
if (selectedNode.hasOwnProperty("@link") == true) {
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"
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…
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?
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 ).
Thanks. This trick saved me time.
Thanks for this post, for the past 30 mins I thought I was going nuts. This did the trick, keep up the good work!
Thanks for saving me a LOT of time which I’d have spent figuring this out myself ^^