Skip to content
Browse files

Support XPath expressions that yield attributes

XPath expressions like '@attr' evaluate to a nodeset containing
attributes.  This would sometimes work in libxmljs (when the libxml
xmlNode for an attribute already had a _private pointing to a libxmljs
attribute wrapper).  But in other cases you could end up with a libxmljs
element wrapper wrapping a libxmljs xmlAttr.  This makes it work in
general.
  • Loading branch information...
1 parent 7bddc0d commit 6376863c09c3f98283778d3e6adef0d5cedde420 David Wragg committed Jul 5, 2012
Showing with 50 additions and 5 deletions.
  1. +2 −0 src/xml_attribute.cc
  2. +1 −1 src/xml_element.cc
  3. +19 −3 src/xml_node.cc
  4. +3 −0 src/xml_node.h
  5. +1 −1 src/xml_xpath_context.cc
  6. +24 −0 test/searching.js
View
2 src/xml_attribute.cc
@@ -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
2 src/xml_element.cc
@@ -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
22 src/xml_node.cc
@@ -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
3 src/xml_node.h
@@ -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
2 src/xml_xpath_context.cc
@@ -41,7 +41,7 @@ XmlXpathContext::evaluate(const xmlChar* xpath) {
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));
+ nodes->Set(i, XmlNode::New(node));
}
xmlXPathFreeObject(result);
View
24 test/searching.js
@@ -14,6 +14,30 @@ 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.find = function(assert) {
var children = [];
var doc = libxml.Document();

0 comments on commit 6376863

Please sign in to comment.
Something went wrong with that request. Please try again.