/* * Document-class: LibXML::XML::XPath * * The XML::XPath module is used to query XML documents. It is * usually accessed via the XML::Document#find or * XML::Node#find methods. For example: * * document.find('/foo', namespaces) -> XML::XPath::Object * * The optional namespaces parameter can be a string, array or * hash table. * * document.find('/foo', 'xlink:http://www.w3.org/1999/xlink') * document.find('/foo', ['xlink:http://www.w3.org/1999/xlink', * 'xi:http://www.w3.org/2001/XInclude') * document.find('/foo', 'xlink' => 'http://www.w3.org/1999/xlink', * 'xi' => 'http://www.w3.org/2001/XInclude') * * * === Working With Default Namespaces * * Finding namespaced elements and attributes can be tricky. * Lets work through an example of a document with a default * namespace: * * * * Phil Bogle's Contacts * * * To find nodes you must define the atom namespace for * libxml. One way to do this is: * * node = doc.find('atom:title', 'atom:http://www.w3.org/2005/Atom') * * Alternatively, you can register the default namespace like this: * * doc.root.namespaces.default_prefix = 'atom' * node = doc.find('atom:title') * * === More Complex Namespace Examples * * Lets work through some more complex examples using the * following xml document: * * * * * * * * * * * * # Since the soap namespace is defined on the root * # node we can directly use it. * doc.find('/soap:Envelope') * * # Since the ns1 namespace is not defined on the root node * # we have to first register it with the xpath engine. * doc.find('//ns1:IdAndName', * 'ns1:http://domain.somewhere.com') * * # Since the getManufacturerNamesResponse element uses a default * # namespace we first have to give it a prefix and register * # it with the xpath engine. * doc.find('//ns:getManufacturerNamesResponse', * 'ns:http://services.somewhere.com') * * # Here is an example showing a complex namespace aware * # xpath expression. * doc.find('/soap:Envelope/soap:Body/ns0:getManufacturerNamesResponse/ns0:IDAndNameList/ns1:IdAndName', * ['ns0:http://services.somewhere.com', 'ns1:http://domain.somewhere.com']) */ #include "ruby_libxml.h" #include VALUE mXPath; VALUE rxml_xpath_to_value(xmlXPathContextPtr xctxt, xmlXPathObjectPtr xobject) { VALUE result; int type; if (xobject == NULL) { /* xmlLastError is different than xctxt->lastError. Use xmlLastError since it has the message set while xctxt->lastError does not. */ xmlErrorPtr xerror = xmlGetLastError(); rxml_raise(xerror); } switch (type = xobject->type) { case XPATH_NODESET: result = rxml_xpath_object_wrap(xctxt->doc, xobject); break; case XPATH_BOOLEAN: result = (xobject->boolval != 0) ? Qtrue : Qfalse; xmlXPathFreeObject(xobject); break; case XPATH_NUMBER: result = rb_float_new(xobject->floatval); xmlXPathFreeObject(xobject); break; case XPATH_STRING: result = rxml_new_cstr(xobject->stringval, xctxt->doc->encoding); xmlXPathFreeObject(xobject); break; default: xmlXPathFreeObject(xobject); rb_raise(rb_eTypeError, "can't convert XPath object of type %d to Ruby value", type ); } return result; } xmlXPathObjectPtr rxml_xpath_from_value(VALUE value) { xmlXPathObjectPtr result = NULL; switch (TYPE(value)) { case T_TRUE: case T_FALSE: result = xmlXPathNewBoolean(RTEST(value)); break; case T_FIXNUM: case T_FLOAT: result = xmlXPathNewFloat(NUM2DBL(value)); break; case T_STRING: result = xmlXPathWrapString(xmlStrdup((const xmlChar *)StringValuePtr(value))); break; case T_NIL: result = xmlXPathNewNodeSet(NULL); break; case T_ARRAY: { long i, j; result = xmlXPathNewNodeSet(NULL); for (i = RARRAY_LEN(value); i > 0; i--) { xmlXPathObjectPtr obj = rxml_xpath_from_value(rb_ary_shift(value)); if ((obj->nodesetval != NULL) && (obj->nodesetval->nodeNr != 0)) { for (j = 0; j < obj->nodesetval->nodeNr; j++) { xmlXPathNodeSetAdd(result->nodesetval, obj->nodesetval->nodeTab[j]); } } } break; } default: rb_raise(rb_eTypeError, "can't convert object of type %s to XPath object", rb_obj_classname(value) ); } return result; } void rxml_init_xpath(void) { mXPath = rb_define_module_under(mXML, "XPath"); /* 0: Undefined value. */ rb_define_const(mXPath, "UNDEFINED", INT2NUM(XPATH_UNDEFINED)); /* 1: A nodeset, will be wrapped by XPath Object. */ rb_define_const(mXPath, "NODESET", INT2NUM(XPATH_NODESET)); /* 2: A boolean value. */ rb_define_const(mXPath, "BOOLEAN", INT2NUM(XPATH_BOOLEAN)); /* 3: A numeric value. */ rb_define_const(mXPath, "NUMBER", INT2NUM(XPATH_NUMBER)); /* 4: A string value. */ rb_define_const(mXPath, "STRING", INT2NUM(XPATH_STRING)); /* 5: An xpointer point */ rb_define_const(mXPath, "POINT", INT2NUM(XPATH_POINT)); /* 6: An xpointer range */ rb_define_const(mXPath, "RANGE", INT2NUM(XPATH_RANGE)); /* 7: An xpointer location set */ rb_define_const(mXPath, "LOCATIONSET", INT2NUM(XPATH_LOCATIONSET)); /* 8: XPath user type */ rb_define_const(mXPath, "USERS", INT2NUM(XPATH_USERS)); /* 9: An XSLT value tree, non modifiable */ rb_define_const(mXPath, "XSLT_TREE", INT2NUM(XPATH_XSLT_TREE)); }