Me Pride! Me Honor!

Arghhhhh! Once again, too many hours wasted debugging a ridiculous oversight!

Got excited about using XPath instead of DOM methods to access XML nodes after browsing through Perl & XML this morning. But then the reaper struck, and grimly. While DOM methods still worked fine, no matter how simple I made my XPath expressions, I could not get them to work from my extension javascript. Meanwhile, equivalent expressions worked just peaches-and-creamily using something like the following (replace path/to/your/file.xml and your/XPath with the path to the XML file you want to traverse and the XPath expression used in the traversal, respectively):

<html>
<head>
<script type="text/javascript">
<!--

// Evaluate an XPath expression aExpression against a given
// DOM node or Document object (aNode), returning the
// results as an array thanks wanderingstan at morethanwarm
// dot mail dot com for the initial work.
function evaluateXPath(aNode, aExpr) {
  var xpe = new XPathEvaluator();
  var nsResolver =
    xpe.createNSResolver(aNode.ownerDocument == null ?
      aNode.documentElement :
      aNode.ownerDocument.documentElement);
  var result = xpe.evaluate(aExpr, aNode, nsResolver,
    0, null);
  var found = [];
  while (res = result.iterateNext())
    found.push(res);
  return found;
}

function getXML() {
  var request = new XMLHttpRequest();
  request.open('GET',
    "file:///path/to/your/file.xml", false);
  request.send(null);
  document.write(request.responseText);
}

function test() {
  var tags = evaluateXPath(document,
    "/html/body/your/XPath");
  var alertText = "";
  for (var i = 0; i < tags.length; i++) {
    alertText += tags[i].nodeName + "\n";
  }
  alert(alertText);
}

-->
</script>
</head>

<body>
<form>
<button type="button" onclick="test();">
XPath DEPLOY!!!
</button>
</form>

<!-- xml data island -->
<script>getXML()</script>
<!-- end data island -->

</body>
</html>

I’ve found the explanation of my woes. If the XML document uses a default namespace (instead of prefixing every tag with a namespace identifier like <html:p/>), you must provide a namepace resolving function when calling evaluate() on a document and XPath expression.

The root element of my XML file looks like this:

<dict-import-template version="0.1"
xmlns="http://www.ttwhy.org/code/bookwurm/dict-import-template-0.1"
template-author="Haw-Bin Chai">

So I needed to write a javascript function like the following:

const CURRENT_VERSION_NAMESPACE =
  "http://www.ttwhy.org/code/bookwurm/" +
  "dict-import-template-0.1";

...

function NSResolver(aPrefix) {
  if(aPrefix == 'bw') {
    return CURRENT_VERSION_NAMESPACE;
  }
  else {
    // this shouldn't ever happen
    return null;
  }
}

call evaluate() from evaluateXPath() like so:

...
var result =
  xpe.evaluate(aExpr, aNode, NSResolver, 0, null);
...

And actually use XPath suchly-thusly:

var results =
  evaluateXPath(importTemplate, "/bw:dict-import-template/*");

Whew.

<pirate>
他妈的我操!
</pirate>

References:

http://kb.mozillazine.org/XPath
http://www.forum4designers.com/archive24-2003-12-16383.html
http://www.faqts.com/knowledge_base/view.phtml/aid/34022/fid/119

Comments are closed.


Bad Behavior has blocked 179 access attempts in the last 7 days.