Permalink
Browse files

Merge pull request #142 from dpw/xpath-results

Support other result types of evaluating XPath expressions
  • Loading branch information...
ncb000gt committed Jul 6, 2012
2 parents 7bddc0d + 3ec6790 commit f4707b5b2ed232b14e9ce7ac6fde450712b9f393
Showing with 99 additions and 23 deletions.
  1. +1 −1 lib/document.js
  2. +6 −1 lib/element.js
  3. +2 −0 src/xml_attribute.cc
  4. +1 −1 src/xml_element.cc
  5. +19 −3 src/xml_node.cc
  6. +3 −0 src/xml_node.h
  7. +31 −16 src/xml_xpath_context.cc
  8. +1 −1 src/xml_xpath_context.h
  9. +35 −0 test/searching.js
View
@@ -37,7 +37,7 @@ Document.prototype.find = function(xpath, ns_uri) {
/// xpath search
/// @return first element matching
Document.prototype.get = function(xpath, ns_uri) {
- return this.find(xpath, ns_uri)[0];
+ return this.root().get(xpath, ns_uri);
};
/// @return a given child
View
@@ -64,7 +64,12 @@ Element.prototype.cdata = function(content) {
};
Element.prototype.get = function() {
- return this.find.apply(this, arguments)[0];
+ var res = this.find.apply(this, arguments);
+ if (res instanceof Array) {
+ return res[0];
+ } else {
+ return res;
+ }
};
Element.prototype.defineNamespace = function(prefix, href) {
View
@@ -30,6 +30,8 @@ XmlAttribute::New(xmlNode* xml_obj, const xmlChar* name, const xmlChar* value)
v8::Handle<v8::Object>
XmlAttribute::New(xmlAttr* attr)
{
+ assert(attr->type == XML_ATTRIBUTE_NODE);
+
if (attr->_private) {
return static_cast<XmlNode*>(attr->_private)->handle_;
}
View
@@ -370,7 +370,7 @@ XmlElement::get_child_nodes() {
uint32_t i = 0;
do {
- children->Set(i, XmlElement::New(child));
+ children->Set(i, XmlNode::New(child));
} while ((child = child->next) && ++i < len);
return scope.Close(children);
View
@@ -163,6 +163,22 @@ XmlNode::Clone(const v8::Arguments& args) {
return scope.Close(node->clone(recurse));
}
+v8::Handle<v8::Value>
+XmlNode::New(xmlNode* node)
+{
+ switch (node->type) {
+ case XML_ATTRIBUTE_NODE:
+ return XmlAttribute::New(reinterpret_cast<xmlAttr *>(node));
+
+ default:
+ // if we don't know how to convert to specific libxmljs wrapper,
+ // wrap in an XmlElement. There should probably be specific
+ // wrapper types for text nodes etc., but this is what existing
+ // code expects.
+ return XmlElement::New(node);
+ }
+}
+
XmlNode::XmlNode(xmlNode* node) : xml_obj(node) {
xml_obj->_private = this;
@@ -259,7 +275,7 @@ v8::Handle<v8::Value>
XmlNode::get_prev_sibling() {
v8::HandleScope scope;
if (xml_obj->prev) {
- return scope.Close(XmlElement::New(xml_obj->prev));
+ return scope.Close(XmlNode::New(xml_obj->prev));
}
return v8::Null();
@@ -269,7 +285,7 @@ v8::Handle<v8::Value>
XmlNode::get_next_sibling() {
v8::HandleScope scope;
if (xml_obj->next) {
- return scope.Close(XmlElement::New(xml_obj->next));
+ return scope.Close(XmlNode::New(xml_obj->next));
}
return v8::Null();
@@ -279,7 +295,7 @@ v8::Handle<v8::Value>
XmlNode::clone(bool recurse) {
v8::HandleScope scope;
- return scope.Close(XmlElement::New(xmlCopyNode(xml_obj, recurse)));
+ return scope.Close(XmlNode::New(xmlCopyNode(xml_obj, recurse)));
}
v8::Handle<v8::Value>
View
@@ -17,6 +17,9 @@ class XmlNode : public node::ObjectWrap {
static void Initialize(v8::Handle<v8::Object> target);
static v8::Persistent<v8::FunctionTemplate> constructor_template;
+ // create new XmlElement, XmlAttribute, etc. to wrap a libxml xmlNode
+ static v8::Handle<v8::Value> New(xmlNode* node);
+
protected:
static v8::Handle<v8::Value> Doc(const v8::Arguments& args);
View
@@ -23,30 +23,45 @@ XmlXpathContext::register_ns(const xmlChar* prefix,
xmlXPathRegisterNs(ctxt, prefix, uri);
}
-v8::Handle<v8::Array>
+v8::Handle<v8::Value>
XmlXpathContext::evaluate(const xmlChar* xpath) {
v8::HandleScope scope;
- xmlXPathObject* result = xmlXPathEval(xpath, ctxt);
+ xmlXPathObject* xpathobj = xmlXPathEval(xpath, ctxt);
+ v8::Handle<v8::Value> res;
- if (!result) {
- return scope.Close(v8::Array::New(0));
- }
+ if (xpathobj) {
+ switch (xpathobj->type) {
+ case XPATH_NODESET: {
+ v8::Handle<v8::Array> nodes = v8::Array::New(xpathobj->nodesetval->nodeNr);
+ for (int i = 0; i != xpathobj->nodesetval->nodeNr; ++i) {
+ nodes->Set(i, XmlNode::New(xpathobj->nodesetval->nodeTab[i]));
+ }
- if (result->type != XPATH_NODESET || !result->nodesetval) {
- xmlXPathFreeObject(result);
+ res = nodes;
+ break;
+ }
- return scope.Close(v8::Array::New(0));
- }
+ case XPATH_BOOLEAN:
+ res = v8::Boolean::New(xpathobj->boolval);
+ break;
- v8::Handle<v8::Array> nodes = v8::Array::New(result->nodesetval->nodeNr);
- for (int i = 0; i != result->nodesetval->nodeNr; ++i) {
- xmlNode* node = result->nodesetval->nodeTab[i];
- nodes->Set(i, XmlElement::New(node));
- }
+ case XPATH_NUMBER:
+ res = v8::Number::New(xpathobj->floatval);
+ break;
- xmlXPathFreeObject(result);
+ case XPATH_STRING:
+ res = v8::String::New((const char *)xpathobj->stringval,
+ xmlStrlen(xpathobj->stringval));
+ break;
+
+ default:
+ res = v8::Null();
+ break;
+ }
+ }
- return scope.Close(nodes);
+ xmlXPathFreeObject(xpathobj);
+ return scope.Close(res);
}
} // namespace libxmljs
View
@@ -15,7 +15,7 @@ class XmlXpathContext {
~XmlXpathContext();
void register_ns(const xmlChar* prefix, const xmlChar* uri);
- v8::Handle<v8::Array> evaluate(const xmlChar* xpath);
+ v8::Handle<v8::Value> evaluate(const xmlChar* xpath);
xmlXPathContext *ctxt;
};
View
@@ -14,6 +14,41 @@ module.exports.get = function(assert) {
assert.done();
};
+module.exports.get_attr = function(assert) {
+ var doc = libxml.Document();
+ var root = doc.node('root');
+ var child = root.node('child');
+ child.attr('attr', 'val');
+ var attr = child.attr('attr');
+
+ // on document
+ assert.equal(attr, doc.get('//@attr'));
+ assert.equal('val', doc.get('//@attr').value());
+
+ // nested
+ assert.equal(attr, doc.get('child').get('@attr'));
+ assert.equal('val', doc.get('child').get('@attr').value());
+
+ // check again after re-parsign the doc
+ doc = libxml.parseXmlString(doc.toString())
+ assert.equal('val', doc.get('//@attr').value());
+ assert.equal('val', doc.get('child').get('@attr').value());
+ assert.equal(doc.get('child'), doc.get('//@attr').node());
+
+ assert.done();
+};
+
+module.exports.get_non_nodeset = function(assert) {
+ var doc = libxml.Document();
+ var root = doc.node('root');
+
+ assert.equal(true, doc.get('true()'));
+ assert.equal(false, doc.get('false()'));
+ assert.equal('Hello, world!', doc.get('"Hello, world!"'));
+ assert.equal(1.23, doc.get('1.23'));
+ assert.done();
+};
+
module.exports.find = function(assert) {
var children = [];
var doc = libxml.Document();

0 comments on commit f4707b5

Please sign in to comment.