While porting some xpath-heavy AS2 code to AS3, I ran into problems. E4X (new and improved xml support in AS3) seems to make xpath obsolete. However, all the E4X query examples in the docs use hard-coded literals. I can't use literals because I don't know at code-time what the queries are going to be. My AS2 code creates xpath paths dynamically from variables. What is the E4X equivalent?
After some initial confusion on my part, I now get it. In retrospect, what's below seems obvious -- but that's the way it always is.
Selecting a variable-named node type
Let's work with this xml:
To find out the the auburn-haired baboon's favorite food, the e4x query is:
Now, if the favorite grouping is a variable, we replace the dot syntax with good-old array/bracket syntax. This is the normal way of accessing variable-named properties on an object.
Just be careful with the dots. Notice there is no dot before the opening bracket.
Variables in a filter
Let's variable-ize this hard-coded query:
and make the attribute and value variables.
Each dot separates a step. Each step has an input and an output XMLList. If the step is a node type, the output list will be a subset of the input list's children, i.e. child nodes with that node type.
If the step is an expression in parentheses, the output list is a filtered subset of the input list itself (not it's children).
If the expression in the parentheses evaluates to true, the 'current node' will be included in the output XMLList. The current node is an XML object. This expression is evaluated once for every node in the incoming XML list.
Calling custom functions
We are not limited to simple comparisons. This example in the docs gives us a hint of what's possible:
It is possible to do more than simple comparisons of attributes and node names to values. We can create custom functions and give them the current node to decide whether to keep that node. For example:
Note: you might think that you can refer to the current node as 'this'. You can't. 'this' is scoped to the containing code. To get at the current node, use valueOf() (a function of the XML class).
BTW, when I first saw this XML.valueOf function in the docs, I could not figure out why on earth you would ever need a function that returns the object itself. Seemed pointless. But now I know.
Of course, the above example is trivial and you would not really implement it that way. It would be better to simply compare the attribute with a value. But using a custom filter function opens infinite possibilities. For example, take a look at this mxml file.
And here's the result:
Here, we're using a second xml file to affect the query of our first xml file. Basically, we're linking database tables. I think that's cool.
Port of XPathAPI
Before I came to my senses on how this worked, I actually ported the XPathAPI code to ActionScript 3 and used it in my project. After I figured out how E4X really works, I went back to my code to remove the XPath code.
But I found that in some cases, I liked the xpath code better. The way I wrote the original code -- lots of string manipulation of the xpath paths, arrays of these strings, etc. -- it just turned out that way and using E4X navigation was actually more awkward. So I left the XPathAPI code in.
In case you find yourself in a similar situation, here's the ported XPathAPI code. You might find this useful when porting AS2 to AS3.
