[ 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 data_out.error = new Error("XML schema parse failure"); 135 } 136 137 return data_out; 138 }, 139 140 /** 141 * Get an XPath-specified value for a given field from an XML node or document. 142 * 143 * @method _getLocationValue 144 * @param field {String | Object} Field definition. 145 * @param context {Object} XML node or document to search within. 146 * @return {Object} Data value or null. 147 * @static 148 * @protected 149 */ 150 _getLocationValue: function(field, context) { 151 var locator = field.locator || field.key || field, 152 xmldoc = context.ownerDocument || context, 153 result, res, value = null; 154 155 try { 156 result = SchemaXML._getXPathResult(locator, context, xmldoc); 157 while ((res = result.iterateNext())) { 158 value = res.textContent || res.value || res.text || res.innerHTML || res.innerText || null; 159 } 160 161 // FIXME: Why defer to a method that is mixed into this object? 162 // DSchema.Base is mixed into DSchema.XML (et al), so 163 // DSchema.XML.parse(...) will work. This supports the use case 164 // where DSchema.Base.parse is changed, and that change is then 165 // seen by all DSchema.* implementations, but does not support the 166 // case where redefining DSchema.XML.parse changes behavior. In 167 // fact, DSchema.XML.parse is never even called. 168 return Y.DataSchema.Base.parse.call(this, value, field); 169 } catch (e) { 170 } 171 172 return null; 173 }, 174 175 /** 176 * Fetches the XPath-specified result for a given location in an XML node 177 * or document. 178 * 179 * @method _getXPathResult 180 * @param locator {String} The XPath location. 181 * @param context {Object} XML node or document to search within. 182 * @param xmldoc {Object} XML document to resolve namespace. 183 * @return {Object} Data collection or null. 184 * @static 185 * @protected 186 */ 187 _getXPathResult: function(locator, context, xmldoc) { 188 // Standards mode 189 if (! Lang.isUndefined(xmldoc.evaluate)) { 190 return xmldoc.evaluate(locator, context, xmldoc.createNSResolver(context.ownerDocument ? context.ownerDocument.documentElement : context.documentElement), 0, null); 191 192 } 193 // IE mode 194 else { 195 var values=[], locatorArray = locator.split(/\b\/\b/), i=0, l=locatorArray.length, location, subloc, m, isNth; 196 197 // XPath is supported 198 try { 199 // this fixes the IE 5.5+ issue where childnode selectors begin at 0 instead of 1 200 try { 201 xmldoc.setProperty("SelectionLanguage", "XPath"); 202 } catch (e) {} 203 204 values = context.selectNodes(locator); 205 } 206 // Fallback for DOM nodes and fragments 207 catch (e) { 208 // Iterate over each locator piece 209 for (; i<l && context; i++) { 210 location = locatorArray[i]; 211 212 // grab nth child [] 213 if ((location.indexOf("[") > -1) && (location.indexOf("]") > -1)) { 214 subloc = location.slice(location.indexOf("[")+1, location.indexOf("]")); 215 //XPath is 1-based while DOM is 0-based 216 subloc--; 217 context = context.children[subloc]; 218 isNth = true; 219 } 220 // grab attribute value @ 221 else if (location.indexOf("@") > -1) { 222 subloc = location.substr(location.indexOf("@")); 223 context = subloc ? context.getAttribute(subloc.replace('@', '')) : context; 224 } 225 // grab that last instance of tagName 226 else if (-1 < location.indexOf("//")) { 227 subloc = context.getElementsByTagName(location.substr(2)); 228 context = subloc.length ? subloc[subloc.length - 1] : null; 229 } 230 // find the last matching location in children 231 else if (l != i + 1) { 232 for (m=context.childNodes.length-1; 0 <= m; m-=1) { 233 if (location === context.childNodes[m].tagName) { 234 context = context.childNodes[m]; 235 m = -1; 236 } 237 } 238 } 239 } 240 241 if (context) { 242 // attribute 243 if (Lang.isString(context)) { 244 values[0] = {value: context}; 245 } 246 // nth child 247 else if (isNth) { 248 values[0] = {value: context.innerHTML}; 249 } 250 // all children 251 else { 252 values = Y.Array(context.childNodes, 0, true); 253 } 254 } 255 } 256 257 // returning a mock-standard object for IE 258 return { 259 index: 0, 260 261 iterateNext: function() { 262 if (this.index >= this.values.length) {return undefined;} 263 var result = this.values[this.index]; 264 this.index += 1; 265 return result; 266 }, 267 268 values: values 269 }; 270 } 271 }, 272 273 /** 274 * Schema-parsed result field. 275 * 276 * @method _parseField 277 * @param field {String | Object} Required. Field definition. 278 * @param result {Object} Required. Schema parsed data object. 279 * @param context {Object} Required. XML node or document to search within. 280 * @static 281 * @protected 282 */ 283 _parseField: function(field, result, context) { 284 var key = field.key || field, 285 parsed; 286 287 if (field.schema) { 288 parsed = { results: [], meta: {} }; 289 parsed = SchemaXML._parseResults(field.schema, context, parsed); 290 291 result[key] = parsed.results; 292 } else { 293 result[key] = SchemaXML._getLocationValue(field, context); 294 } 295 }, 296 297 /** 298 * Parses results data according to schema 299 * 300 * @method _parseMeta 301 * @param xmldoc_in {Object} XML document parse. 302 * @param data_out {Object} In-progress schema-parsed data to update. 303 * @return {Object} Schema-parsed data. 304 * @static 305 * @protected 306 */ 307 _parseMeta: function(metaFields, xmldoc_in, data_out) { 308 if(Lang.isObject(metaFields)) { 309 var key, 310 xmldoc = xmldoc_in.ownerDocument || xmldoc_in; 311 312 for(key in metaFields) { 313 if (metaFields.hasOwnProperty(key)) { 314 data_out.meta[key] = SchemaXML._getLocationValue(metaFields[key], xmldoc); 315 } 316 } 317 } 318 return data_out; 319 }, 320 321 /** 322 * Schema-parsed result to add to results list. 323 * 324 * @method _parseResult 325 * @param fields {Array} Required. A collection of field definition. 326 * @param context {Object} Required. XML node or document to search within. 327 * @return {Object} Schema-parsed data. 328 * @static 329 * @protected 330 */ 331 _parseResult: function(fields, context) { 332 var result = {}, j; 333 334 // Find each field value 335 for (j=fields.length-1; 0 <= j; j--) { 336 SchemaXML._parseField(fields[j], result, context); 337 } 338 339 return result; 340 }, 341 342 /** 343 * Schema-parsed list of results from full data 344 * 345 * @method _parseResults 346 * @param schema {Object} Schema to parse against. 347 * @param context {Object} XML node or document to parse. 348 * @param data_out {Object} In-progress schema-parsed data to update. 349 * @return {Object} Schema-parsed data. 350 * @static 351 * @protected 352 */ 353 _parseResults: function(schema, context, data_out) { 354 if (schema.resultListLocator && Lang.isArray(schema.resultFields)) { 355 var xmldoc = context.ownerDocument || context, 356 fields = schema.resultFields, 357 results = [], 358 node, nodeList, i=0; 359 360 if (schema.resultListLocator.match(/^[:\-\w]+$/)) { 361 nodeList = context.getElementsByTagName(schema.resultListLocator); 362 363 // loop through each result node 364 for (i = nodeList.length - 1; i >= 0; --i) { 365 results[i] = SchemaXML._parseResult(fields, nodeList[i]); 366 } 367 } else { 368 nodeList = SchemaXML._getXPathResult(schema.resultListLocator, context, xmldoc); 369 370 // loop through the nodelist 371 while ((node = nodeList.iterateNext())) { 372 results[i] = SchemaXML._parseResult(fields, node); 373 i += 1; 374 } 375 } 376 377 if (results.length) { 378 data_out.results = results; 379 } else { 380 data_out.error = new Error("XML schema result nodes retrieval failure"); 381 } 382 } 383 return data_out; 384 } 385 }; 386 387 Y.DataSchema.XML = Y.mix(SchemaXML, Y.DataSchema.Base); 388 389 390 }, '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 |