X

Sundararajan's Weblog

  • Java
    November 10, 2005

Easy-to-use XPath API for JavaScript

Guest Author

Yes, E4X is not included in Mustang's Rhino JavaScript engine.
But, we have rich XML API in JDK. We can
easily use Java API to create reusable JavaScript functions for XML. We will see how we can write
nice XPath scripting API using
javax.xml.xpath
package.


function XPath(expr) {
var global = XPath.__parent__;
function isJSName(qname) {
return qname.namespaceURI == 'http://www.mozilla.org/rhino/';
}
// We create a XPathVariableResolver that resolves
// variables names as script global variables.
function varResolver() {
return new Packages.javax.xml.xpath.XPathVariableResolver() {
resolveVariable: function(qname) {
if (isJSName(qname)) {
return global[qname.localPart];
}
return null;
}
}
}
// We create a XPathFunctionResolver that resolves
// function names as script global functions.
function funcResolver() {
return new Packages.javax.xml.xpath.XPathFunctionResolver() {
resolveFunction: function(qname, arity) {
// ignore arity...
if (isJSName(qname) &&
typeof(global[qname.localPart]) == 'function') {
// implement XPathFunction that calls the underlying
// JavaScript function
return new Packages.javax.xml.xpath.XPathFunction() {
evaluate: function(argsList) {
var func = global[qname.localPart];
var argsArray = argsList.toArray();
var args = new Array(argsArray.length);
for (var i in argsArray) {
args[i] = argsArray[i];
}
return func.apply(global, args);
}
}
}
return null;
}
}
}
// create a NamespaceContext
// we map mozilla URL to "js" prefix
function namespaceCtx() {
return new Packages.javax.xml.namespace.NamespaceContext() {
getNamespaceURI: function(prefix) {
switch (prefix) {
case "xml":
return "http://www.w3.org/XML/1998/namespace";
case "xmlns":
return "http://www.w3.org/2000/xmlns/"
case "js":
return "http://www.mozilla.org/rhino/";
default:
return "";
}
},
getPrefix: function(uri) {
switch (uri) {
case "http://www.w3.org/XML/1998/namespace":
return "xml";
case "http://www.w3.org/2000/xmlns/":
return "xmlns"
case "http://www.mozilla.org/rhino/":
return "js";
default:
return "";
}
}
}
}
var xpath = Packages.javax.xml.xpath.XPathFactory.newInstance().newXPath();
xpath.setXPathVariableResolver(varResolver());
xpath.setXPathFunctionResolver(funcResolver());
xpath.setNamespaceContext(namespaceCtx());
return xpath.compile(expr);
}
function XMLSource(file) {
return new Packages.org.xml.sax.InputSource(java.io.FileInputStream(file));
}

We'll use the following simple XML file ("t.xml") as example:
    <html>
<head>
<title>hello</title>
<body>
<img src="mustang.jpg"></img>
</body>
</head>
</html>

Using the above XPath and XMLSource functions, we can write something like:

function toUpper(s) {
return s.toUpperCase();
}
var src = XMLSource("t.xml");
var xp = XPath("js:toUpper(/html/head/title)");
print(xp.evaluate(src)); // prints "HELLO".

In the above code, we make use of user defined JavaScript function toUpper
inside XPath expression. We can easily write useful script functions and
call the same in XPath expressions. I've 'js' namespace prefix for
JavaScript functions and variables. So, you need to use the prefix "js:"
to call script functions. This prefix may be changed by editing XPath
function given above. Also, as a convention, I've used Rhino URL as URI for
"js" prefix.

Join the discussion

Comments ( 2 )
  • guest Wednesday, November 16, 2005
    please please include e4x in Mustang
  • Alex Wednesday, November 30, 2005

    I really cannot be convinced that the above way or any of the existing Java XML APIs can be as easy to use and manipulate then E4X.

    And since the footprint argument is rather weak (it's only 400KB extra using standard techniques to bundle with the JRE) so please consider putting this proper component of Rhino back into Mustang!

Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.