|
1 | | -/* |
2 | | - * Document-class: LibXML::XML::XPath |
3 | | - * |
4 | | - * The XML::XPath module is used to query XML documents. It is |
5 | | - * usually accessed via the XML::Document#find or |
6 | | - * XML::Node#find methods. For example: |
7 | | - * |
8 | | - * document.find('/foo', namespaces) -> XML::XPath::Object |
9 | | - * |
10 | | - * The optional namespaces parameter can be a string, array or |
11 | | - * hash table. |
12 | | - * |
13 | | - * document.find('/foo', 'xlink:http://www.w3.org/1999/xlink') |
14 | | - * document.find('/foo', ['xlink:http://www.w3.org/1999/xlink', |
15 | | - * 'xi:http://www.w3.org/2001/XInclude') |
16 | | - * document.find('/foo', 'xlink' => 'http://www.w3.org/1999/xlink', |
17 | | - * 'xi' => 'http://www.w3.org/2001/XInclude') |
18 | | - * |
19 | | - * |
20 | | - * === Working With Default Namespaces |
21 | | - * |
22 | | - * Finding namespaced elements and attributes can be tricky. |
23 | | - * Lets work through an example of a document with a default |
24 | | - * namespace: |
25 | | - * |
26 | | - * <?xml version="1.0" encoding="utf-8"?> |
27 | | - * <feed xmlns="http://www.w3.org/2005/Atom"> |
28 | | - * <title type="text">Phil Bogle's Contacts</title> |
29 | | - * </feed> |
30 | | - * |
31 | | - * To find nodes you must define the atom namespace for |
32 | | - * libxml. One way to do this is: |
33 | | - * |
34 | | - * node = doc.find('atom:title', 'atom:http://www.w3.org/2005/Atom') |
35 | | - * |
36 | | - * Alternatively, you can register the default namespace like this: |
37 | | - * |
38 | | - * doc.root.namespaces.default_prefix = 'atom' |
39 | | - * node = doc.find('atom:title') |
40 | | - * |
41 | | - * === More Complex Namespace Examples |
42 | | - * |
43 | | - * Lets work through some more complex examples using the |
44 | | - * following xml document: |
45 | | - * |
46 | | - * <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> |
47 | | - * <soap:Body> |
48 | | - * <getManufacturerNamesResponse xmlns="http://services.somewhere.com"> |
49 | | - * <IDAndNameList xmlns="http://services.somewhere.com"> |
50 | | - * <ns1:IdAndName xmlns:ns1="http://domain.somewhere.com"/> |
51 | | - * </IDAndNameList> |
52 | | - * </getManufacturerNamesResponse> |
53 | | - * </soap:Body> |
54 | | - * </soap:Envelope> |
55 | | - * |
56 | | - * # Since the soap namespace is defined on the root |
57 | | - * # node we can directly use it. |
58 | | - * doc.find('/soap:Envelope') |
59 | | - * |
60 | | - * # Since the ns1 namespace is not defined on the root node |
61 | | - * # we have to first register it with the xpath engine. |
62 | | - * doc.find('//ns1:IdAndName', |
63 | | - * 'ns1:http://domain.somewhere.com') |
64 | | - * |
65 | | - * # Since the getManufacturerNamesResponse element uses a default |
66 | | - * # namespace we first have to give it a prefix and register |
67 | | - * # it with the xpath engine. |
68 | | - * doc.find('//ns:getManufacturerNamesResponse', |
69 | | - * 'ns:http://services.somewhere.com') |
70 | | - * |
71 | | - * # Here is an example showing a complex namespace aware |
72 | | - * # xpath expression. |
73 | | - * doc.find('/soap:Envelope/soap:Body/ns0:getManufacturerNamesResponse/ns0:IDAndNameList/ns1:IdAndName', |
74 | | - * ['ns0:http://services.somewhere.com', 'ns1:http://domain.somewhere.com']) |
75 | | -*/ |
76 | | - |
77 | | -#include "ruby_libxml.h" |
78 | | -#include <libxml/xpathInternals.h> |
79 | | - |
80 | | -VALUE mXPath; |
81 | | - |
82 | | -VALUE rxml_xpath_to_value(xmlXPathContextPtr xctxt, xmlXPathObjectPtr xobject) |
83 | | -{ |
84 | | - VALUE result; |
85 | | - int type; |
86 | | - |
87 | | - if (xobject == NULL) |
88 | | - { |
89 | | - /* xmlLastError is different than xctxt->lastError. Use |
90 | | - xmlLastError since it has the message set while xctxt->lastError |
91 | | - does not. */ |
92 | | - const xmlError *xerror = xmlGetLastError(); |
93 | | - rxml_raise(xerror); |
94 | | - } |
95 | | - |
96 | | - switch (type = xobject->type) |
97 | | - { |
98 | | - case XPATH_NODESET: |
99 | | - result = rxml_xpath_object_wrap(xctxt->doc, xobject); |
100 | | - break; |
101 | | - case XPATH_BOOLEAN: |
102 | | - result = (xobject->boolval != 0) ? Qtrue : Qfalse; |
103 | | - xmlXPathFreeObject(xobject); |
104 | | - break; |
105 | | - case XPATH_NUMBER: |
106 | | - result = rb_float_new(xobject->floatval); |
107 | | - xmlXPathFreeObject(xobject); |
108 | | - break; |
109 | | - case XPATH_STRING: |
110 | | - result = rxml_new_cstr(xobject->stringval, xctxt->doc->encoding); |
111 | | - xmlXPathFreeObject(xobject); |
112 | | - break; |
113 | | - default: |
114 | | - xmlXPathFreeObject(xobject); |
115 | | - rb_raise(rb_eTypeError, |
116 | | - "can't convert XPath object of type %d to Ruby value", type |
117 | | - ); |
118 | | - } |
119 | | - |
120 | | - return result; |
121 | | -} |
122 | | - |
123 | | -xmlXPathObjectPtr rxml_xpath_from_value(VALUE value) |
124 | | -{ |
125 | | - xmlXPathObjectPtr result = NULL; |
126 | | - |
127 | | - switch (TYPE(value)) |
128 | | - { |
129 | | - case T_TRUE: |
130 | | - case T_FALSE: |
131 | | - result = xmlXPathNewBoolean(RTEST(value)); |
132 | | - break; |
133 | | - case T_FIXNUM: |
134 | | - case T_FLOAT: |
135 | | - result = xmlXPathNewFloat(NUM2DBL(value)); |
136 | | - break; |
137 | | - case T_STRING: |
138 | | - result = xmlXPathWrapString(xmlStrdup((const xmlChar *)StringValuePtr(value))); |
139 | | - break; |
140 | | - case T_NIL: |
141 | | - result = xmlXPathNewNodeSet(NULL); |
142 | | - break; |
143 | | - case T_ARRAY: |
144 | | - { |
145 | | - long i, j; |
146 | | - result = xmlXPathNewNodeSet(NULL); |
147 | | - |
148 | | - for (i = RARRAY_LEN(value); i > 0; i--) |
149 | | - { |
150 | | - xmlXPathObjectPtr obj = rxml_xpath_from_value(rb_ary_shift(value)); |
151 | | - |
152 | | - if ((obj->nodesetval != NULL) && (obj->nodesetval->nodeNr != 0)) |
153 | | - { |
154 | | - for (j = 0; j < obj->nodesetval->nodeNr; j++) |
155 | | - { |
156 | | - xmlXPathNodeSetAdd(result->nodesetval, obj->nodesetval->nodeTab[j]); |
157 | | - } |
158 | | - } |
159 | | - } |
160 | | - break; |
161 | | - } |
162 | | - default: |
163 | | - rb_raise(rb_eTypeError, |
164 | | - "can't convert object of type %s to XPath object", rb_obj_classname(value) |
165 | | - ); |
166 | | - } |
167 | | - |
168 | | - return result; |
169 | | -} |
170 | | - |
171 | | -void rxml_init_xpath(void) |
172 | | -{ |
173 | | - mXPath = rb_define_module_under(mXML, "XPath"); |
174 | | - |
175 | | - /* 0: Undefined value. */ |
176 | | - rb_define_const(mXPath, "UNDEFINED", INT2NUM(XPATH_UNDEFINED)); |
177 | | - /* 1: A nodeset, will be wrapped by XPath Object. */ |
178 | | - rb_define_const(mXPath, "NODESET", INT2NUM(XPATH_NODESET)); |
179 | | - /* 2: A boolean value. */ |
180 | | - rb_define_const(mXPath, "BOOLEAN", INT2NUM(XPATH_BOOLEAN)); |
181 | | - /* 3: A numeric value. */ |
182 | | - rb_define_const(mXPath, "NUMBER", INT2NUM(XPATH_NUMBER)); |
183 | | - /* 4: A string value. */ |
184 | | - rb_define_const(mXPath, "STRING", INT2NUM(XPATH_STRING)); |
185 | | - /* 5: An xpointer point */ |
186 | | - rb_define_const(mXPath, "POINT", INT2NUM(XPATH_POINT)); |
187 | | - /* 6: An xpointer range */ |
188 | | - rb_define_const(mXPath, "RANGE", INT2NUM(XPATH_RANGE)); |
189 | | - /* 7: An xpointer location set */ |
190 | | - rb_define_const(mXPath, "LOCATIONSET", INT2NUM(XPATH_LOCATIONSET)); |
191 | | - /* 8: XPath user type */ |
192 | | - rb_define_const(mXPath, "USERS", INT2NUM(XPATH_USERS)); |
193 | | - /* 9: An XSLT value tree, non modifiable */ |
194 | | - rb_define_const(mXPath, "XSLT_TREE", INT2NUM(XPATH_XSLT_TREE)); |
195 | | -} |
| 1 | +/* |
| 2 | + * Document-class: LibXML::XML::XPath |
| 3 | + * |
| 4 | + * The XML::XPath module is used to query XML documents. It is |
| 5 | + * usually accessed via the XML::Document#find or |
| 6 | + * XML::Node#find methods. For example: |
| 7 | + * |
| 8 | + * document.find('/foo', namespaces) -> XML::XPath::Object |
| 9 | + * |
| 10 | + * The optional namespaces parameter can be a string, array or |
| 11 | + * hash table. |
| 12 | + * |
| 13 | + * document.find('/foo', 'xlink:http://www.w3.org/1999/xlink') |
| 14 | + * document.find('/foo', ['xlink:http://www.w3.org/1999/xlink', |
| 15 | + * 'xi:http://www.w3.org/2001/XInclude') |
| 16 | + * document.find('/foo', 'xlink' => 'http://www.w3.org/1999/xlink', |
| 17 | + * 'xi' => 'http://www.w3.org/2001/XInclude') |
| 18 | + * |
| 19 | + * |
| 20 | + * === Working With Default Namespaces |
| 21 | + * |
| 22 | + * Finding namespaced elements and attributes can be tricky. |
| 23 | + * Lets work through an example of a document with a default |
| 24 | + * namespace: |
| 25 | + * |
| 26 | + * <?xml version="1.0" encoding="utf-8"?> |
| 27 | + * <feed xmlns="http://www.w3.org/2005/Atom"> |
| 28 | + * <title type="text">Phil Bogle's Contacts</title> |
| 29 | + * </feed> |
| 30 | + * |
| 31 | + * To find nodes you must define the atom namespace for |
| 32 | + * libxml. One way to do this is: |
| 33 | + * |
| 34 | + * node = doc.find('atom:title', 'atom:http://www.w3.org/2005/Atom') |
| 35 | + * |
| 36 | + * Alternatively, you can register the default namespace like this: |
| 37 | + * |
| 38 | + * doc.root.namespaces.default_prefix = 'atom' |
| 39 | + * node = doc.find('atom:title') |
| 40 | + * |
| 41 | + * === More Complex Namespace Examples |
| 42 | + * |
| 43 | + * Lets work through some more complex examples using the |
| 44 | + * following xml document: |
| 45 | + * |
| 46 | + * <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> |
| 47 | + * <soap:Body> |
| 48 | + * <getManufacturerNamesResponse xmlns="http://services.somewhere.com"> |
| 49 | + * <IDAndNameList xmlns="http://services.somewhere.com"> |
| 50 | + * <ns1:IdAndName xmlns:ns1="http://domain.somewhere.com"/> |
| 51 | + * </IDAndNameList> |
| 52 | + * </getManufacturerNamesResponse> |
| 53 | + * </soap:Body> |
| 54 | + * </soap:Envelope> |
| 55 | + * |
| 56 | + * # Since the soap namespace is defined on the root |
| 57 | + * # node we can directly use it. |
| 58 | + * doc.find('/soap:Envelope') |
| 59 | + * |
| 60 | + * # Since the ns1 namespace is not defined on the root node |
| 61 | + * # we have to first register it with the xpath engine. |
| 62 | + * doc.find('//ns1:IdAndName', |
| 63 | + * 'ns1:http://domain.somewhere.com') |
| 64 | + * |
| 65 | + * # Since the getManufacturerNamesResponse element uses a default |
| 66 | + * # namespace we first have to give it a prefix and register |
| 67 | + * # it with the xpath engine. |
| 68 | + * doc.find('//ns:getManufacturerNamesResponse', |
| 69 | + * 'ns:http://services.somewhere.com') |
| 70 | + * |
| 71 | + * # Here is an example showing a complex namespace aware |
| 72 | + * # xpath expression. |
| 73 | + * doc.find('/soap:Envelope/soap:Body/ns0:getManufacturerNamesResponse/ns0:IDAndNameList/ns1:IdAndName', |
| 74 | + * ['ns0:http://services.somewhere.com', 'ns1:http://domain.somewhere.com']) |
| 75 | +*/ |
| 76 | + |
| 77 | +#include "ruby_libxml.h" |
| 78 | +#include <libxml/xpathInternals.h> |
| 79 | + |
| 80 | +VALUE mXPath; |
| 81 | + |
| 82 | +VALUE rxml_xpath_to_value(xmlXPathContextPtr xctxt, xmlXPathObjectPtr xobject) |
| 83 | +{ |
| 84 | + VALUE result; |
| 85 | + int type; |
| 86 | + |
| 87 | + if (xobject == NULL) |
| 88 | + { |
| 89 | + /* xmlLastError is different than xctxt->lastError. Use |
| 90 | + xmlLastError since it has the message set while xctxt->lastError |
| 91 | + does not. */ |
| 92 | + const xmlError *xerror = xmlGetLastError(); |
| 93 | + rxml_raise(xerror); |
| 94 | + } |
| 95 | + |
| 96 | + switch (type = xobject->type) |
| 97 | + { |
| 98 | + case XPATH_NODESET: |
| 99 | + result = rxml_xpath_object_wrap(xctxt->doc, xobject); |
| 100 | + break; |
| 101 | + case XPATH_BOOLEAN: |
| 102 | + result = (xobject->boolval != 0) ? Qtrue : Qfalse; |
| 103 | + xmlXPathFreeObject(xobject); |
| 104 | + break; |
| 105 | + case XPATH_NUMBER: |
| 106 | + result = rb_float_new(xobject->floatval); |
| 107 | + xmlXPathFreeObject(xobject); |
| 108 | + break; |
| 109 | + case XPATH_STRING: |
| 110 | + result = rxml_new_cstr(xobject->stringval, xctxt->doc->encoding); |
| 111 | + xmlXPathFreeObject(xobject); |
| 112 | + break; |
| 113 | + default: |
| 114 | + xmlXPathFreeObject(xobject); |
| 115 | + rb_raise(rb_eTypeError, |
| 116 | + "can't convert XPath object of type %d to Ruby value", type |
| 117 | + ); |
| 118 | + } |
| 119 | + |
| 120 | + return result; |
| 121 | +} |
| 122 | + |
| 123 | +xmlXPathObjectPtr rxml_xpath_from_value(VALUE value) |
| 124 | +{ |
| 125 | + xmlXPathObjectPtr result = NULL; |
| 126 | + |
| 127 | + switch (TYPE(value)) |
| 128 | + { |
| 129 | + case T_TRUE: |
| 130 | + case T_FALSE: |
| 131 | + result = xmlXPathNewBoolean(RTEST(value)); |
| 132 | + break; |
| 133 | + case T_FIXNUM: |
| 134 | + case T_FLOAT: |
| 135 | + result = xmlXPathNewFloat(NUM2DBL(value)); |
| 136 | + break; |
| 137 | + case T_STRING: |
| 138 | + result = xmlXPathWrapString(xmlStrdup((const xmlChar *)StringValuePtr(value))); |
| 139 | + break; |
| 140 | + case T_NIL: |
| 141 | + result = xmlXPathNewNodeSet(NULL); |
| 142 | + break; |
| 143 | + case T_ARRAY: |
| 144 | + { |
| 145 | + long i, j; |
| 146 | + result = xmlXPathNewNodeSet(NULL); |
| 147 | + |
| 148 | + for (i = RARRAY_LEN(value); i > 0; i--) |
| 149 | + { |
| 150 | + xmlXPathObjectPtr obj = rxml_xpath_from_value(rb_ary_shift(value)); |
| 151 | + |
| 152 | + if ((obj->nodesetval != NULL) && (obj->nodesetval->nodeNr != 0)) |
| 153 | + { |
| 154 | + for (j = 0; j < obj->nodesetval->nodeNr; j++) |
| 155 | + { |
| 156 | + xmlXPathNodeSetAdd(result->nodesetval, obj->nodesetval->nodeTab[j]); |
| 157 | + } |
| 158 | + } |
| 159 | + } |
| 160 | + break; |
| 161 | + } |
| 162 | + default: |
| 163 | + rb_raise(rb_eTypeError, |
| 164 | + "can't convert object of type %s to XPath object", rb_obj_classname(value) |
| 165 | + ); |
| 166 | + } |
| 167 | + |
| 168 | + return result; |
| 169 | +} |
| 170 | + |
| 171 | +void rxml_init_xpath(void) |
| 172 | +{ |
| 173 | + mXPath = rb_define_module_under(mXML, "XPath"); |
| 174 | + |
| 175 | + /* 0: Undefined value. */ |
| 176 | + rb_define_const(mXPath, "UNDEFINED", INT2NUM(XPATH_UNDEFINED)); |
| 177 | + /* 1: A nodeset, will be wrapped by XPath Object. */ |
| 178 | + rb_define_const(mXPath, "NODESET", INT2NUM(XPATH_NODESET)); |
| 179 | + /* 2: A boolean value. */ |
| 180 | + rb_define_const(mXPath, "BOOLEAN", INT2NUM(XPATH_BOOLEAN)); |
| 181 | + /* 3: A numeric value. */ |
| 182 | + rb_define_const(mXPath, "NUMBER", INT2NUM(XPATH_NUMBER)); |
| 183 | + /* 4: A string value. */ |
| 184 | + rb_define_const(mXPath, "STRING", INT2NUM(XPATH_STRING)); |
| 185 | + /* 5: An xpointer point */ |
| 186 | + rb_define_const(mXPath, "POINT", INT2NUM(XPATH_POINT)); |
| 187 | + /* 6: An xpointer range */ |
| 188 | + rb_define_const(mXPath, "RANGE", INT2NUM(XPATH_RANGE)); |
| 189 | + /* 7: An xpointer location set */ |
| 190 | + rb_define_const(mXPath, "LOCATIONSET", INT2NUM(XPATH_LOCATIONSET)); |
| 191 | + /* 8: XPath user type */ |
| 192 | + rb_define_const(mXPath, "USERS", INT2NUM(XPATH_USERS)); |
| 193 | + /* 9: An XSLT value tree, non modifiable */ |
| 194 | + rb_define_const(mXPath, "XSLT_TREE", INT2NUM(XPATH_XSLT_TREE)); |
| 195 | +} |
0 commit comments