« Friday Macrochat: Introducing the Flash Player Detection Kit | Main | runtime data binding article »

January 31, 2005

Using XPath with Flash

A blog post of mine from yesteryear, but the issue came up recently (and the class is undocumented) so I thought I'd reblog it.

Flash MX 2004 introduced the XPathAPI class which allows you to do simple XPath searches within Flash. This can be very useful for searching XML packets based on node names and attribute values.

In order to use XPath searches within Flash you first need to include the XPathAPI class into your Flash library by including the DataBindingClass if it hasn't been included already. If you've already set up bindings this class may have been included automatically, otherwise you'll need to select the class from the common libraries by selecting Window > Other Panels > Common Libraries > Classes. From the Classes.fla library panel you can simply drag a copy of the DataBindingClasses component into your current Flash document's library. Now you can import the class by typing "import mx.xpath.XPathAPI;" or by using the classes fully qualified name when accessing its methods by prefixing the class' methods with mx.xpath.XPathAPI.".

The XPath class has two methods; selectNodeList and selectSingleNode. The selectNodeList method returns an array of XML nodes matching the XPath expression, whereas the selectSingleNode returns only the first matching node.

You can see an example of the XPath API in ActionScript below, before you can test the movie you have to make sre that you have a copy of the DataBindingClasses component in your movie's library by dragging it from Common Libraries > Classes into your current Flash movie's library.

import mx.xpath.XPathAPI;
var rssfeed_xml:XML = new XML();
rssfeed_xml.ignoreWhite = true;
rssfeed_xml.load("http://www.markme.com/dehaan/index.rdf");
rssfeed_xml.onLoad = function(success:Boolean) {
  if (success) {
    var titlePath:String = "/rdf:RDF/item/title";
    var title_array:Array = XPathAPI.selectNodeList(this.firstChild, titlePath);
    for (var i = 0; i<title_array.length; i++) {
      trace(title_array[i].firstChild.nodeValue);
    }
  } else {
    trace("error loading XML");
  }
};

If you want more info on this class, check out Nate Weiss' book here.

Posted by jdehaan at January 31, 2005 07:21 PM

Comments

Don't know if this is of interest...

As I didn't know of the above class's existance, I've been using xFactorStudio's XPath implementation, and have found it to be quite useful.

http://www.xfactorstudio.com

direct link is:
http://www.xfactorstudio.com/ActionScript/AS2/XPath/

btw, how complete an implementation is the built in (XPathAPI) class?

Posted by: Richard T-J at January 31, 2005 08:02 PM

Whoops, there is an error in the code sample:

It seems that using a less than symbol (<) in your blog causes it to wipe anything else on that line...

import mx.xpath.XPathAPI;
var rssfeed_xml = new XML();
rssfeed_xml.ignoreWhite = true;
rssfeed_xml.load("http://www.markme.com/dehaan/index.rdf");
rssfeed_xml.onLoad = function(success) {
if (success) {
var titlePath:String = "/rdf:RDF/item/title";
var title_array:Array = mx.xpath.XPathAPI.selectNodeList(this.firstChild, titlePath);
var titleCount:Number = title_array.length;
for (var i = 0; i < itleCount; i++) {
trace(title_array[i].firstChild.nodeValue);
}
} else {
trace("error loading XML");
}
};

Posted by: Richard T-J at January 31, 2005 08:14 PM

Thanks Richard, I updated the code in the entry.

Sorry about that.

Posted by: jdehaan at January 31, 2005 09:21 PM

Correcting the Richard T-J code, much better this one.
import mx.xpath.XPathAPI;
var rssfeed_xml = new XML();
rssfeed_xml.ignoreWhite = true;
rssfeed_xml.load("http://www.markme.com/dehaan/index.rdf");
rssfeed_xml.onLoad = function(success) {
if (success) {
var titlePath:String = "/rdf:RDF/item/title";
var title_array:Array = mx.xpath.XPathAPI.selectNodeList(this.firstChild, titlePath);
var titleCount:Number = title_array.length;
for (var i = 0; i < titleCount; i++) {
trace(title_array[i].firstChild.nodeValue);
}
} else {
trace("error loading XML");
}
};

Posted by: Igor Costa at January 31, 2005 09:23 PM

shameless plug: another example can be found here :)

http://www.richinternet.de/blog/index.cfm?entry=22368700-0603-4CC0-21D1749BDAEC4632

Dirk.

Posted by: Dirk Eismann at February 1, 2005 12:58 AM

Thanks, Jen, I didn't know that was there... good news.

But why was it "undocumented"? Was it experimental or intended, and did it pass testing or not? What context would people have to put around those literal commands in order to use them confidently...?

tx,
jd

Posted by: John Dowdell at February 1, 2005 08:43 AM

Very cool Jen. That will definitely save some time being able to set/get the root element like that.

Posted by: brandon at February 1, 2005 11:57 AM

If you have ActionScript Viewer installed, you can browse the XPathAPI class and see the following methods listed, although I'm not sure how many are actually useful (apart from selectNodeList and selectSingleNode):

class mx.xpath.XPathAPI {
  function XPathAPI () {}
  static function getEvalString(node, path) {}
  static function selectNodeList(node, path) {}
  static function selectSingleNode(node, path) {}
  static function setNodeValue(node, path, newValue) {}
  static function copyStack(toStk, fromStk) {}
  static function evalExpr(expr, node) {}
  static function filterNodes(nodeList, stack) {}
  static function getAllChildNodesByName(nodeList, name) {}
  static function getChildNodeByName(node, nodeName) {}
  static function getKeyValues(node, keySpec) {}
  static function getPath(node, keySpecs) {}
  static function getPathSet(path) {}
}

Posted by: Hector at February 1, 2005 02:06 PM

I'll try to answer earlier queries, although note I'm guessing becuase I wasn't around during the 2004 release. I'd guess the class was tested due to its inclusion in the components. I'm not sure about the story on documentation though. I will file a bug about this to make sure we all look into it.

Posted by: jdehaan at February 2, 2005 10:35 AM

important to note that of the above-mentioned methods, these re the only public ones:

getEvalString(node:XMLNode, path:String);

selectNodeList(node:XMLNode, path:String):Array;

selectSingleNode(node:XMLNode, path:String);

setNodeValue(node:XMLNode, path:String, newValue:String):Number

the others will say ''i'm private''

Posted by: mike at October 8, 2005 08:45 PM

Hi, Any ideas on how to use XPATH in a CLASS?

Posted by: sajid at December 2, 2005 06:05 AM

I've got a project that was done in MX2004 and uses XFactor Studio to get the value of attributes in nodes. So far, I've had no luck getting XFactor's XPath API to work in Flash 8. Does anyone know if there's a way to retreive the value of attributes using the Flash XPath API?

Posted by: gjennings at December 30, 2005 10:34 AM

Ola!

It is not quite working as I thought.. maybe someone can share a light. This is my xml:



16
12
13
14


And this is my AS copied straight from here:
var rssfeed_xml:XML = new XML();
rssfeed_xml.ignoreWhite = true;
rssfeed_xml.load("ref.xml");
rssfeed_xml.onLoad = function(success:Boolean) {
if (success) {
var titlePath:String = "/refList/category";
var title_array:Array = XPathAPI.selectNodeList(this.firstChild, titlePath);
for (var i = 0; i<title_array.length; i++) {
trace(title_array[i].firstChild.nodeValue);
}
} else {
trace("error loading XML");
}
};

When I publish this, it traces:
null
null

Why?

In reality what I was trying to do was to get all documents that matches a catId...
could I do something like this:
/refList/category/@ID==2
?

Posted by: Moca at January 22, 2006 09:27 PM

Ahhh I got it to work like this, after saving their library:
import com.xfactorstudio.xml.xpath.*;

var myDoc = new XPathDocument();
myDoc.onLoad = function()
{
//select the first node
var selected_items = myDoc.selectNodes("//category[@id='1']");
//
//trace the length of items
trace("The length of this XML file is "+selected_items.length)
//
for(var i:Number=0; i<=selected_items.length-1;i++)
{
trace(selected_items[i]);
}
}
myDoc.load("refDeskTable.xml");

Posted by: Moca at January 26, 2006 04:59 PM

Hi all,

I am wondering if any of you knows how much of xpath is implemented in the XPathAPI of Flash8.

For instance, queries like

/a/b/c[contains(@someattr,'somestring')]

or

/a/b/c[@someattr='@somestring]/..

or

/a/b[c[@someattr='@somestring]]

which work in other xpath implementations such as MSXML .NET and which are standardized by w3c, will always return no results.

I am writting this after having tested such queries on multiple implementations on the same documents.

Thanx a bunch!

Posted by: Catalin Stan at July 23, 2006 03:50 PM

I already use XPath tech in my .net applications and also use Stylus Studio 2006 for accurately composing my XPath expressions. Both and all other tools I use agree on the same standards of the language (XPath). The damn question I always ask is: Why Flash insists on being different and being far away from standards.
The tiny bit of XPath that Macromedia applied in Flash is very different than the true XPath. I hope that Adobe make things more cleaner and more proffesional.

Posted by: George Said at August 12, 2006 12:02 PM

I am new with Flash8.

(i) can any one tell me how much Xpath has been supported in Flash8.

(ii) how to use Xpath with ActionScript2.0 (any tutorial / example ) ?

(iii) can guide me about its any documentation ?
Thanking in advance.

Posted by: mayur.GTL at September 5, 2006 11:02 PM

This answers most of your questions (support in Flash 8, how to use, and documentation):

http://download.macromedia.com/pub/documentation/en/flash/fl8/XpathAPI.pdf

Posted by: Jen at September 6, 2006 01:21 PM

I want to know that The "setNodeValue" statement how to update attribute?

thanks in advanced

Posted by: xtempy at March 22, 2007 01:18 AM

so guys, can you tell me how to retrieve the attribute ? there aren't any article explaining this at all, could you help me out?

for example,
var titlePath:String = "/rdf:RDF/item/title@name";
var titlePath:String = "/rdf:RDF/item/title/@name";
var titlePath:String = "/rdf:RDF/item/title[@name]";
I tried all, but I cannot get the title attribute! how can I do this?

Posted by: komekome at April 6, 2007 10:20 PM

Same question here: How to to access attributes? Did anyone find the answer to this?

Posted by: Marjorie at April 8, 2007 12:07 PM

This has been driving me crazy for about a month. I'm not using this for RSS but I needed to extract the node attributes for comparison. It doesn't appear that there is any XPath syntax that will allow you to retrieve an attribute value directly in one line of code. You can filter the nodes by attribute values (i.e. option[@id=1] ) but you can't return an attribute value using the public XPathAPI methods.

Here is how I was able to get the "id" attribute for all of my nodes. You should be able to just substitute your XPath for mine and change "id" to "name":

// Use selectNodeList() from XPathAPI

var relatedNodes_array = new Array();
var relatedNodes_array = XPathAPI.selectNodeList(this.firstChild, "/homes/model/option[@id=1]/related_options");

/* Convert Array of length 1 back to XML and then convert the smaller XML tree into an array to get its unknown length. If you already know the length then there is no need for this extra step */

var relatedNodesXML:XML = relatedNodes_array[0];
var temp_array = relatedNodesXML.childNodes;

for(i=0; i<temp_array.length; i++){
trace( relatedNodesXML.childNodes[i].attributes["id"] ); // prints only the id values (1, 2, 3...)
}


Hopefully this helps some of you. If anyone finds a better solution please post it here.

Posted by: Dylan at April 12, 2007 11:49 AM

i use the example download at xfactorstudio XPath4AS2.zip

imagine in the myrss.xml , the item has an attribute call img

at line:25, changing adding nodeValue to its attributes

rssData.push({Author:author_array[i].firstChild.nodeValue, Title:title_array[i].firstChild.nodeValue});

rssData.push({Author:author_array[i].firstChild.nodeValue, Title:title_array[i].attributes.img});

finished!

Posted by: yau at April 13, 2007 10:47 AM

hi,

Marjorie, you can simple add .attributes["id"] in your selectSingleNode statement to get the attribute :),

Macromedia forget to mention attributes Array attach to each XMLnode they coded :)

Posted by: Sumit Gupta at August 21, 2007 03:32 AM

Hi, I came across your blog and found it really interesting.
I have started to use XPath in Flash for a project and I am new to it.

Please can you advise me on how to get help with the attached file?
Its basically going to be a series of text tooltips that will display on mouse over of a button.

My AS is:

‘====================================================
import com.xfactorstudio.xml.xpath.*;
var areasXML : XML;
areasXML = new XML();
areasXML.ignoreWhite = true;
areasXML.onLoad = function(success:Boolean) {
area1_path = XPath.selectNodes(areasXML, "/areas/area[name='Bolton']");
area2_path = XPath.selectNodes(areasXML, "/areas/area[name='Blackburn']");
area3_path = XPath.selectNodes(areasXML, "/areas/area[name='Manchester']");
Bolton_Text.text = area1_path;
Blackburn_Text.text = area2_path;
Manchester_Text.text = area3_path;
};
areasXML.load("areas.xml");
‘====================================================


Many thanks!

Posted by: Roger at January 23, 2008 03:34 AM

Post a comment




Remember Me?