1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
|
/*
* 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:
*
* <?xml version="1.0" encoding="utf-8"?>
* <feed xmlns="http://www.w3.org/2005/Atom">
* <title type="text">Phil Bogle's Contacts</title>
* </feed>
*
* 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:
*
* <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
* <soap:Body>
* <getManufacturerNamesResponse xmlns="http://services.somewhere.com">
* <IDAndNameList xmlns="http://services.somewhere.com">
* <ns1:IdAndName xmlns:ns1="http://domain.somewhere.com"/>
* </IDAndNameList>
* </getManufacturerNamesResponse>
* </soap:Envelope>
*
* # 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"
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((const char*)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: {
int 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));
}
|