[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
1 /* 2 YUI 3.17.2 (build 9c3c78e) 3 Copyright 2014 Yahoo! Inc. All rights reserved. 4 Licensed under the BSD License. 5 http://yuilibrary.com/license/ 6 */ 7 8 YUI.add('dataschema-xml', function (Y, NAME) { 9 10 /** 11 Provides a DataSchema implementation which can be used to work with XML data. 12 13 @module dataschema 14 @submodule dataschema-xml 15 **/ 16 17 /** 18 Provides a DataSchema implementation which can be used to work with XML data. 19 20 See the `apply` method for usage. 21 22 @class DataSchema.XML 23 @extends DataSchema.Base 24 @static 25 **/ 26 var Lang = Y.Lang, 27 28 okNodeType = { 29 1 : true, 30 9 : true, 31 11: true 32 }, 33 34 SchemaXML; 35 36 SchemaXML = { 37 38 //////////////////////////////////////////////////////////////////////////// 39 // 40 // DataSchema.XML static methods 41 // 42 //////////////////////////////////////////////////////////////////////////// 43 /** 44 Applies a schema to an XML data tree, returning a normalized object with 45 results in the `results` property. Additional information can be parsed out 46 of the XML for inclusion in the `meta` property of the response object. If 47 an error is encountered during processing, an `error` property will be 48 added. 49 50 Field data in the nodes captured by the XPath in _schema.resultListLocator_ 51 is extracted with the field identifiers described in _schema.resultFields_. 52 Field identifiers are objects with the following properties: 53 54 * `key` : <strong>(required)</strong> The desired property name to use 55 store the retrieved value in the result object. If `locator` is 56 not specified, `key` is also used as the XPath locator (String) 57 * `locator`: The XPath locator to the node or attribute within each 58 result node found by _schema.resultListLocator_ containing the 59 desired field data (String) 60 * `parser` : A function or the name of a function on `Y.Parsers` used 61 to convert the input value into a normalized type. Parser 62 functions are passed the value as input and are expected to 63 return a value. 64 * `schema` : Used to retrieve nested field data into an array for 65 assignment as the result field value. This object follows the same 66 conventions as _schema_. 67 68 If no value parsing or nested parsing is needed, you can use XPath locators 69 (strings) instead of field identifiers (objects) -- see example below. 70 71 `response.results` will contain an array of objects with key:value pairs. 72 The keys are the field identifier `key`s, and the values are the data 73 values extracted from the nodes or attributes found by the field `locator` 74 (or `key` fallback). 75 76 To extract additional information from the XML, include an array of 77 XPath locators in _schema.metaFields_. The collected values will be 78 stored in `response.meta` with the XPath locator as keys. 79 80 @example 81 var schema = { 82 resultListLocator: '//produce/item', 83 resultFields: [ 84 { 85 locator: 'name', 86 key: 'name' 87 }, 88 { 89 locator: 'color', 90 key: 'color', 91 parser: function (val) { return val.toUpperCase(); } 92 } 93 ] 94 }; 95 96 // Assumes data like 97 // <inventory> 98 // <produce> 99 // <item><name>Banana</name><color>yellow</color></item> 100 // <item><name>Orange</name><color>orange</color></item> 101 // <item><name>Eggplant</name><color>purple</color></item> 102 // </produce> 103 // </inventory> 104 105 var response = Y.DataSchema.JSON.apply(schema, data); 106 107 // response.results[0] is { name: "Banana", color: "YELLOW" } 108 109 @method apply 110 @param {Object} schema Schema to apply. Supported configuration 111 properties are: 112 @param {String} [schema.resultListLocator] XPath locator for the 113 XML nodes that contain the data to flatten into `response.results` 114 @param {Array} [schema.resultFields] Field identifiers to 115 locate/assign values in the response records. See above for 116 details. 117 @param {Array} [schema.metaFields] XPath locators to extract extra 118 non-record related information from the XML data 119 @param {XMLDocument} data XML data to parse 120 @return {Object} An Object with properties `results` and `meta` 121 @static 122 **/ 123 apply: function(schema, data) { 124 var xmldoc = data, // unnecessary variables 125 data_out = { results: [], meta: {} }; 126 127 if (xmldoc && okNodeType[xmldoc.nodeType] && schema) { 128 // Parse results data 129 data_out = SchemaXML._parseResults(schema, xmldoc, data_out); 130 131 // Parse meta data 132 data_out = SchemaXML._parseMeta(schema.metaFields, xmldoc, data_out); 133 } else { 134 Y.log("XML data could not be schema-parsed: " + Y.dump(data) + " " + Y.dump(data), "error", "dataschema-xml"); 135 data_out.error = new Error("XML schema parse failure"); 136 } 137 138 return data_out; 139 }, 140 141 /** 142 * Get an XPath-specified value for a given field from an XML node or document. 143 * 144 * @method _getLocationValue 145 * @param field {String | Object} Field definition. 146 * @param context {Object} XML node or document to search within. 147 * @return {Object} Data value or null. 148 * @static 149 * @protected 150 */ 151 _getLocationValue: function(field, context) { 152 var locator = field.locator || field.key || field, 153 xmldoc = context.ownerDocument || context, 154 result, res, value = null; 155 156 try { 157 result = SchemaXML._getXPathResult(locator, context, xmldoc); 158 while ((res = result.iterateNext())) { 159 value = res.textContent || res.value || res.text || res.innerHTML || res.innerText || null; 160 } 161 162 // FIXME: Why defer to a method that is mixed into this object? 163 // DSchema.Base is mixed into DSchema.XML (et al), so 164 // DSchema.XML.parse(...) will work. This supports the use case 165 // where DSchema.Base.parse is changed, and that change is then 166 // seen by all DSchema.* implementations, but does not support the 167 // case where redefining DSchema.XML.parse changes behavior. In 168 // fact, DSchema.XML.parse is never even called. 169 return Y.DataSchema.Base.parse.call(this, value, field); 170 } catch (e) { 171 Y.log('SchemaXML._getLocationValue failed: ' + e.message); 172 } 173 174 return null; 175 }, 176 177 /** 178 * Fetches the XPath-specified result for a given location in an XML node 179 * or document. 180 * 181 * @method _getXPathResult 182 * @param locator {String} The XPath location. 183 * @param context {Object} XML node or document to search within. 184 * @param xmldoc {Object} XML document to resolve namespace. 185 * @return {Object} Data collection or null. 186 * @static 187 * @protected 188 */ 189 _getXPathResult: function(locator, context, xmldoc) { 190 // Standards mode 191 if (! Lang.isUndefined(xmldoc.evaluate)) { 192 return xmldoc.evaluate(locator, context, xmldoc.createNSResolver(context.ownerDocument ? context.ownerDocument.documentElement : context.documentElement), 0, null); 193 194 } 195 // IE mode 196 else { 197 var values=[], locatorArray = locator.split(/\b\/\b/), i=0, l=locatorArray.length, location, subloc, m, isNth; 198 199 // XPath is supported 200 try { 201 // this fixes the IE 5.5+ issue where childnode selectors begin at 0 instead of 1 202 try { 203 xmldoc.setProperty("SelectionLanguage", "XPath"); 204 } catch (e) {} 205 206 values = context.selectNodes(locator); 207 } 208 // Fallback for DOM nodes and fragments 209 catch (e) { 210 // Iterate over each locator piece 211 for (; i<l && context; i++) { 212 location = locatorArray[i]; 213 214 // grab nth child [] 215 if ((location.indexOf("[") > -1) && (location.indexOf("]") > -1)) { 216 subloc = location.slice(location.indexOf("[")+1, location.indexOf("]")); 217 //XPath is 1-based while DOM is 0-based 218 subloc--; 219 context = context.children[subloc]; 220 isNth = true; 221 } 222 // grab attribute value @ 223 else if (location.indexOf("@") > -1) { 224 subloc = location.substr(location.indexOf("@")); 225 context = subloc ? context.getAttribute(subloc.replace('@', '')) : context; 226 } 227 // grab that last instance of tagName 228 else if (-1 < location.indexOf("//")) { 229 subloc = context.getElementsByTagName(location.substr(2)); 230 context = subloc.length ? subloc[subloc.length - 1] : null; 231 } 232 // find the last matching location in children 233 else if (l != i + 1) { 234 for (m=context.childNodes.length-1; 0 <= m; m-=1) { 235 if (location === context.childNodes[m].tagName) { 236 context = context.childNodes[m]; 237 m = -1; 238 } 239 } 240 } 241 } 242 243 if (context) { 244 // attribute 245 if (Lang.isString(context)) { 246 values[0] = {value: context}; 247 } 248 // nth child 249 else if (isNth) { 250 values[0] = {value: context.innerHTML}; 251 } 252 // all children 253 else { 254 values = Y.Array(context.childNodes, 0, true); 255 } 256 } 257 } 258 259 // returning a mock-standard object for IE 260 return { 261 index: 0, 262 263 iterateNext: function() { 264 if (this.index >= this.values.length) {return undefined;} 265 var result = this.values[this.index]; 266 this.index += 1; 267 return result; 268 }, 269 270 values: values 271 }; 272 } 273 }, 274 275 /** 276 * Schema-parsed result field. 277 * 278 * @method _parseField 279 * @param field {String | Object} Required. Field definition. 280 * @param result {Object} Required. Schema parsed data object. 281 * @param context {Object} Required. XML node or document to search within. 282 * @static 283 * @protected 284 */ 285 _parseField: function(field, result, context) { 286 var key = field.key || field, 287 parsed; 288 289 if (field.schema) { 290 parsed = { results: [], meta: {} }; 291 parsed = SchemaXML._parseResults(field.schema, context, parsed); 292 293 result[key] = parsed.results; 294 } else { 295 result[key] = SchemaXML._getLocationValue(field, context); 296 } 297 }, 298 299 /** 300 * Parses results data according to schema 301 * 302 * @method _parseMeta 303 * @param xmldoc_in {Object} XML document parse. 304 * @param data_out {Object} In-progress schema-parsed data to update. 305 * @return {Object} Schema-parsed data. 306 * @static 307 * @protected 308 */ 309 _parseMeta: function(metaFields, xmldoc_in, data_out) { 310 if(Lang.isObject(metaFields)) { 311 var key, 312 xmldoc = xmldoc_in.ownerDocument || xmldoc_in; 313 314 for(key in metaFields) { 315 if (metaFields.hasOwnProperty(key)) { 316 data_out.meta[key] = SchemaXML._getLocationValue(metaFields[key], xmldoc); 317 } 318 } 319 } 320 return data_out; 321 }, 322 323 /** 324 * Schema-parsed result to add to results list. 325 * 326 * @method _parseResult 327 * @param fields {Array} Required. A collection of field definition. 328 * @param context {Object} Required. XML node or document to search within. 329 * @return {Object} Schema-parsed data. 330 * @static 331 * @protected 332 */ 333 _parseResult: function(fields, context) { 334 var result = {}, j; 335 336 // Find each field value 337 for (j=fields.length-1; 0 <= j; j--) { 338 SchemaXML._parseField(fields[j], result, context); 339 } 340 341 return result; 342 }, 343 344 /** 345 * Schema-parsed list of results from full data 346 * 347 * @method _parseResults 348 * @param schema {Object} Schema to parse against. 349 * @param context {Object} XML node or document to parse. 350 * @param data_out {Object} In-progress schema-parsed data to update. 351 * @return {Object} Schema-parsed data. 352 * @static 353 * @protected 354 */ 355 _parseResults: function(schema, context, data_out) { 356 if (schema.resultListLocator && Lang.isArray(schema.resultFields)) { 357 var xmldoc = context.ownerDocument || context, 358 fields = schema.resultFields, 359 results = [], 360 node, nodeList, i=0; 361 362 if (schema.resultListLocator.match(/^[:\-\w]+$/)) { 363 nodeList = context.getElementsByTagName(schema.resultListLocator); 364 365 // loop through each result node 366 for (i = nodeList.length - 1; i >= 0; --i) { 367 results[i] = SchemaXML._parseResult(fields, nodeList[i]); 368 } 369 } else { 370 nodeList = SchemaXML._getXPathResult(schema.resultListLocator, context, xmldoc); 371 372 // loop through the nodelist 373 while ((node = nodeList.iterateNext())) { 374 results[i] = SchemaXML._parseResult(fields, node); 375 i += 1; 376 } 377 } 378 379 if (results.length) { 380 data_out.results = results; 381 } else { 382 data_out.error = new Error("XML schema result nodes retrieval failure"); 383 } 384 } 385 return data_out; 386 } 387 }; 388 389 Y.DataSchema.XML = Y.mix(SchemaXML, Y.DataSchema.Base); 390 391 392 }, '3.17.2', {"requires": ["dataschema-base"]});
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Aug 11 10:00:09 2016 | Cross-referenced by PHPXref 0.7.1 |