1/* 2 Copyright 2011-2013 Abdulla Abdurakhmanov 3 Original sources are available at https://code.google.com/p/x2js/ 4 5 Licensed under the Apache License, Version 2.0 (the "License"); 6 you may not use this file except in compliance with the License. 7 You may obtain a copy of the License at 8 9 http://www.apache.org/licenses/LICENSE-2.0 10 11 Unless required by applicable law or agreed to in writing, software 12 distributed under the License is distributed on an "AS IS" BASIS, 13 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 See the License for the specific language governing permissions and 15 limitations under the License. 16 */ 17 18(function (root, factory) { 19 if (typeof define === "function" && define.amd) { 20 define([], factory); 21 } else if (typeof exports === "object") { 22 module.exports = factory(); 23 } else { 24 root.X2JS = factory(); 25 } 26 }(this, function () { 27 return function (config) { 28 'use strict'; 29 30 var VERSION = "1.2.0"; 31 32 config = config || {}; 33 initConfigDefaults(); 34 initRequiredPolyfills(); 35 36 function initConfigDefaults() { 37 if(config.escapeMode === undefined) { 38 config.escapeMode = true; 39 } 40 41 config.attributePrefix = config.attributePrefix || "_"; 42 config.arrayAccessForm = config.arrayAccessForm || "none"; 43 config.emptyNodeForm = config.emptyNodeForm || "text"; 44 45 if(config.enableToStringFunc === undefined) { 46 config.enableToStringFunc = true; 47 } 48 config.arrayAccessFormPaths = config.arrayAccessFormPaths || []; 49 if(config.skipEmptyTextNodesForObj === undefined) { 50 config.skipEmptyTextNodesForObj = true; 51 } 52 if(config.stripWhitespaces === undefined) { 53 config.stripWhitespaces = true; 54 } 55 config.datetimeAccessFormPaths = config.datetimeAccessFormPaths || []; 56 57 if(config.useDoubleQuotes === undefined) { 58 config.useDoubleQuotes = false; 59 } 60 61 config.xmlElementsFilter = config.xmlElementsFilter || []; 62 config.jsonPropertiesFilter = config.jsonPropertiesFilter || []; 63 64 if(config.keepCData === undefined) { 65 config.keepCData = false; 66 } 67 } 68 69 var DOMNodeTypes = { 70 ELEMENT_NODE : 1, 71 TEXT_NODE : 3, 72 CDATA_SECTION_NODE : 4, 73 COMMENT_NODE : 8, 74 DOCUMENT_NODE : 9 75 }; 76 77 function initRequiredPolyfills() { 78 } 79 80 function getNodeLocalName( node ) { 81 var nodeLocalName = node.localName; 82 if(nodeLocalName == null) // Yeah, this is IE!! 83 nodeLocalName = node.baseName; 84 if(nodeLocalName == null || nodeLocalName=="") // =="" is IE too 85 nodeLocalName = node.nodeName; 86 return nodeLocalName; 87 } 88 89 function getNodePrefix(node) { 90 return node.prefix; 91 } 92 93 function escapeXmlChars(str) { 94 if(typeof(str) == "string") 95 return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, '''); 96 else 97 return str; 98 } 99 100 function unescapeXmlChars(str) { 101 return str.replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, "'").replace(/&/g, '&'); 102 } 103 104 function checkInStdFiltersArrayForm(stdFiltersArrayForm, obj, name, path) { 105 var idx = 0; 106 for(; idx < stdFiltersArrayForm.length; idx++) { 107 var filterPath = stdFiltersArrayForm[idx]; 108 if( typeof filterPath === "string" ) { 109 if(filterPath == path) 110 break; 111 } 112 else 113 if( filterPath instanceof RegExp) { 114 if(filterPath.test(path)) 115 break; 116 } 117 else 118 if( typeof filterPath === "function") { 119 if(filterPath(obj, name, path)) 120 break; 121 } 122 } 123 return idx!=stdFiltersArrayForm.length; 124 } 125 126 function toArrayAccessForm(obj, childName, path) { 127 switch(config.arrayAccessForm) { 128 case "property": 129 if(!(obj[childName] instanceof Array)) 130 obj[childName+"_asArray"] = [obj[childName]]; 131 else 132 obj[childName+"_asArray"] = obj[childName]; 133 break; 134 /*case "none": 135 break;*/ 136 } 137 138 if(!(obj[childName] instanceof Array) && config.arrayAccessFormPaths.length > 0) { 139 if(checkInStdFiltersArrayForm(config.arrayAccessFormPaths, obj, childName, path)) { 140 obj[childName] = [obj[childName]]; 141 } 142 } 143 } 144 145 function fromXmlDateTime(prop) { 146 // Implementation based up on http://stackoverflow.com/questions/8178598/xml-datetime-to-javascript-date-object 147 // Improved to support full spec and optional parts 148 var bits = prop.split(/[-T:+Z]/g); 149 150 var d = new Date(bits[0], bits[1]-1, bits[2]); 151 var secondBits = bits[5].split("\."); 152 d.setHours(bits[3], bits[4], secondBits[0]); 153 if(secondBits.length>1) 154 d.setMilliseconds(secondBits[1]); 155 156 // Get supplied time zone offset in minutes 157 if(bits[6] && bits[7]) { 158 var offsetMinutes = bits[6] * 60 + Number(bits[7]); 159 var sign = /\d\d-\d\d:\d\d$/.test(prop)? '-' : '+'; 160 161 // Apply the sign 162 offsetMinutes = 0 + (sign == '-'? -1 * offsetMinutes : offsetMinutes); 163 164 // Apply offset and local timezone 165 d.setMinutes(d.getMinutes() - offsetMinutes - d.getTimezoneOffset()) 166 } 167 else 168 if(prop.indexOf("Z", prop.length - 1) !== -1) { 169 d = new Date(Date.UTC(d.getFullYear(), d.getMonth(), d.getDate(), d.getHours(), d.getMinutes(), d.getSeconds(), d.getMilliseconds())); 170 } 171 172 // d is now a local time equivalent to the supplied time 173 return d; 174 } 175 176 function checkFromXmlDateTimePaths(value, childName, fullPath) { 177 if(config.datetimeAccessFormPaths.length > 0) { 178 var path = fullPath.split("\.#")[0]; 179 if(checkInStdFiltersArrayForm(config.datetimeAccessFormPaths, value, childName, path)) { 180 return fromXmlDateTime(value); 181 } 182 else 183 return value; 184 } 185 else 186 return value; 187 } 188 189 function checkXmlElementsFilter(obj, childType, childName, childPath) { 190 if( childType == DOMNodeTypes.ELEMENT_NODE && config.xmlElementsFilter.length > 0) { 191 return checkInStdFiltersArrayForm(config.xmlElementsFilter, obj, childName, childPath); 192 } 193 else 194 return true; 195 } 196 197 function parseDOMChildren( node, path ) { 198 if(node.nodeType == DOMNodeTypes.DOCUMENT_NODE) { 199 var result = new Object; 200 var nodeChildren = node.childNodes; 201 // Alternative for firstElementChild which is not supported in some environments 202 for(var cidx=0; cidx <nodeChildren.length; cidx++) {
203 var child = nodeChildren.item(cidx); 204 if(child.nodeType == DOMNodeTypes.ELEMENT_NODE) { 205 var childName = getNodeLocalName(child); 206 result[childName] = parseDOMChildren(child, childName); 207 } 208 } 209 return result; 210 } 211 else 212 if(node.nodeType == DOMNodeTypes.ELEMENT_NODE) { 213 var result = new Object; 214 result.__cnt=0; 215 216 var nodeChildren = node.childNodes; 217 218 // Children nodes 219 for(var cidx=0; cidx <nodeChildren.length; cidx++) { 220 var child = nodeChildren.item(cidx); // nodeChildren[cidx]; 221 var childName = getNodeLocalName(child); 222 223 if(child.nodeType!= DOMNodeTypes.COMMENT_NODE) { 224 var childPath = path+"."+childName; 225 if (checkXmlElementsFilter(result,child.nodeType,childName,childPath)) { 226 result.__cnt++; 227 if(result[childName] == null) { 228 result[childName] = parseDOMChildren(child, childPath); 229 toArrayAccessForm(result, childName, childPath); 230 } 231 else { 232 if(result[childName] != null) { 233 if( !(result[childName] instanceof Array)) { 234 result[childName] = [result[childName]]; 235 toArrayAccessForm(result, childName, childPath); 236 } 237 } 238 (result[childName])[result[childName].length] = parseDOMChildren(child, childPath); 239 } 240 } 241 } 242 } 243 244 // Attributes 245 for(var aidx=0; aidx <node.attributes.length; aidx++) { 246 var attr = node.attributes.item(aidx); // [aidx]; 247 result.__cnt++; 248 result[config.attributePrefix+attr.name]=attr.value; 249 } 250 251 // Node namespace prefix 252 var nodePrefix = getNodePrefix(node); 253 if(nodePrefix!=null && nodePrefix!="") { 254 result.__cnt++; 255 result.__prefix=nodePrefix; 256 } 257 258 if(result["#text"]!=null) { 259 result.__text = result["#text"]; 260 if(result.__text instanceof Array) { 261 result.__text = result.__text.join("\n"); 262 } 263 //if(config.escapeMode) 264 // result.__text = unescapeXmlChars(result.__text); 265 if(config.stripWhitespaces) 266 result.__text = result.__text.trim(); 267 delete result["#text"]; 268 if(config.arrayAccessForm=="property") 269 delete result["#text_asArray"]; 270 result.__text = checkFromXmlDateTimePaths(result.__text, childName, path+"."+childName); 271 } 272 if(result["#cdata-section"]!=null) { 273 result.__cdata = result["#cdata-section"]; 274 delete result["#cdata-section"]; 275 if(config.arrayAccessForm=="property") 276 delete result["#cdata-section_asArray"]; 277 } 278 279 if( result.__cnt == 0 && config.emptyNodeForm=="text" ) { 280 result = ''; 281 } 282 else 283 if( result.__cnt == 1 && result.__text!=null ) { 284 result = result.__text; 285 } 286 else 287 if( result.__cnt == 1 && result.__cdata!=null && !config.keepCData ) { 288 result = result.__cdata; 289 } 290 else 291 if ( result.__cnt > 1 && result.__text!=null && config.skipEmptyTextNodesForObj) { 292 if( (config.stripWhitespaces && result.__text=="") || (result.__text.trim()=="")) { 293 delete result.__text; 294 } 295 } 296 delete result.__cnt; 297 298 if( config.enableToStringFunc && (result.__text!=null || result.__cdata!=null )) { 299 result.toString = function() { 300 return (this.__text!=null? this.__text:'')+( this.__cdata!=null ? this.__cdata:''); 301 }; 302 } 303 304 return result; 305 } 306 else 307 if(node.nodeType == DOMNodeTypes.TEXT_NODE || node.nodeType == DOMNodeTypes.CDATA_SECTION_NODE) { 308 return node.nodeValue; 309 } 310 } 311 312 function startTag(jsonObj, element, attrList, closed) { 313 var resultStr = "<"+ ( (jsonObj!=null && jsonObj.__prefix!=null)? (jsonObj.__prefix+":"):"") + element; 314 if(attrList!=null) { 315 for(var aidx = 0; aidx < attrList.length; aidx++) { 316 var attrName = attrList[aidx]; 317 var attrVal = jsonObj[attrName]; 318 if(config.escapeMode) 319 attrVal=escapeXmlChars(attrVal); 320 resultStr+=" "+attrName.substr(config.attributePrefix.length)+"="; 321 if(config.useDoubleQuotes) 322 resultStr+='"'+attrVal+'"'; 323 else 324 resultStr+="'"+attrVal+"'"; 325 } 326 } 327 if(!closed) 328 resultStr+=">"; 329 else 330 resultStr+="/>"; 331 return resultStr; 332 } 333 334 function endTag(jsonObj,elementName) { 335 return "</"+ (jsonObj.__prefix!=null? (jsonObj.__prefix+":"):"")+elementName+">"; 336 } 337
338 function endsWith(str, suffix) { 339 return str.indexOf(suffix, str.length - suffix.length) !== -1; 340 } 341 342 function jsonXmlSpecialElem ( jsonObj, jsonObjField ) { 343 if((config.arrayAccessForm=="property" && endsWith(jsonObjField.toString(),("_asArray"))) 344 || jsonObjField.toString().indexOf(config.attributePrefix)==0 345 || jsonObjField.toString().indexOf("__")==0 346 || (jsonObj[jsonObjField] instanceof Function) ) 347 return true; 348 else 349 return false; 350 } 351 352 function jsonXmlElemCount ( jsonObj ) { 353 var elementsCnt = 0; 354 if(jsonObj instanceof Object ) { 355 for( var it in jsonObj ) { 356 if(jsonXmlSpecialElem ( jsonObj, it) ) 357 continue; 358 elementsCnt++; 359 } 360 } 361 return elementsCnt; 362 } 363 364 function checkJsonObjPropertiesFilter(jsonObj, propertyName, jsonObjPath) { 365 return config.jsonPropertiesFilter.length == 0 366 || jsonObjPath=="" 367 || checkInStdFiltersArrayForm(config.jsonPropertiesFilter, jsonObj, propertyName, jsonObjPath); 368 } 369 370 function parseJSONAttributes ( jsonObj ) { 371 var attrList = []; 372 if(jsonObj instanceof Object ) { 373 for( var ait in jsonObj ) { 374 if(ait.toString().indexOf("__")== -1 && ait.toString().indexOf(config.attributePrefix)==0) { 375 attrList.push(ait); 376 } 377 } 378 } 379 return attrList; 380 } 381 382 function parseJSONTextAttrs ( jsonTxtObj ) { 383 var result =""; 384 385 if(jsonTxtObj.__cdata!=null) { 386 result+="<![CDATA["+jsonTxtObj.__cdata+"]]>"; 387 } 388 389 if(jsonTxtObj.__text!=null) { 390 if(config.escapeMode) 391 result+=escapeXmlChars(jsonTxtObj.__text); 392 else 393 result+=jsonTxtObj.__text; 394 } 395 return result; 396 } 397 398 function parseJSONTextObject ( jsonTxtObj ) { 399 var result =""; 400 401 if( jsonTxtObj instanceof Object ) { 402 result+=parseJSONTextAttrs ( jsonTxtObj ); 403 } 404 else 405 if(jsonTxtObj!=null) { 406 if(config.escapeMode) 407 result+=escapeXmlChars(jsonTxtObj); 408 else 409 result+=jsonTxtObj; 410 } 411 412 return result; 413 } 414 415 function getJsonPropertyPath(jsonObjPath, jsonPropName) { 416 if (jsonObjPath==="") { 417 return jsonPropName; 418 } 419 else 420 return jsonObjPath+"."+jsonPropName; 421 } 422 423 function parseJSONArray ( jsonArrRoot, jsonArrObj, attrList, jsonObjPath ) { 424 var result = ""; 425 if(jsonArrRoot.length == 0) { 426 result+=startTag(jsonArrRoot, jsonArrObj, attrList, true); 427 } 428 else { 429 for(var arIdx = 0; arIdx < jsonArrRoot.length; arIdx++) { 430 result+=startTag(jsonArrRoot[arIdx], jsonArrObj, parseJSONAttributes(jsonArrRoot[arIdx]), false); 431 result+=parseJSONObject(jsonArrRoot[arIdx], getJsonPropertyPath(jsonObjPath,jsonArrObj)); 432 result+=endTag(jsonArrRoot[arIdx],jsonArrObj); 433 } 434 } 435 return result; 436 } 437 438 function parseJSONObject ( jsonObj, jsonObjPath ) { 439 var result = ""; 440 441 var elementsCnt = jsonXmlElemCount ( jsonObj ); 442 443 if(elementsCnt > 0) { 444 for( var it in jsonObj ) { 445 446 if(jsonXmlSpecialElem ( jsonObj, it) || (jsonObjPath!="" && !checkJsonObjPropertiesFilter(jsonObj, it, getJsonPropertyPath(jsonObjPath,it))) ) 447 continue; 448 449 var subObj = jsonObj[it]; 450 451 var attrList = parseJSONAttributes( subObj ) 452 453 if(subObj == null || subObj == undefined) { 454 result+=startTag(subObj, it, attrList, true); 455 } 456 else 457 if(subObj instanceof Object) { 458 459 if(subObj instanceof Array) { 460 result+=parseJSONArray( subObj, it, attrList, jsonObjPath ); 461 } 462 else if(subObj instanceof Date) { 463 result+=startTag(subObj, it, attrList, false); 464 result+=subObj.toISOString(); 465 result+=endTag(subObj,it); 466 } 467 else { 468 var subObjElementsCnt = jsonXmlElemCount ( subObj ); 469 if(subObjElementsCnt > 0 || subObj.__text!=null || subObj.__cdata!=null) { 470 result+=startTag(subObj, it, attrList, false); 471 result+=parseJSONObject(subObj, getJsonPropertyPath(jsonObjPath,it)); 472 result+=endTag(subObj,it); 473 } 474 else { 475 result+=startTag(subObj, it, attrList, true); 476 } 477 } 478 } 479 else { 480 result+=startTag(subObj, it, attrList, false); 481 result+=parseJSONTextObject(subObj); 482 result+=endTag(subObj,it); 483 } 484 } 485 } 486 result+=parseJSONTextObject(jsonObj); 487 488 return result; 489 } 490 491 this.parseXmlString = function(xmlDocStr) { 492 var isIEParser = window.ActiveXObject || "ActiveXObject" in window; 493 if (xmlDocStr === undefined) { 494 return null; 495 } 496 var xmlDoc; 497 if (window.DOMParser) { 498 var parser=new window.DOMParser(); 499 var parsererrorNS = null; 500 // IE9+ now is here 501 if(!isIEParser) { 502 try { 503 parsererrorNS = parser.parseFromString("INVALID", "text/xml").getElementsByTagName("parsererror")[0].namespaceURI; 504 } 505 catch(err) { 506 parsererrorNS = null; 507 } 508 }
509 try { 510 xmlDoc = parser.parseFromString( xmlDocStr, "text/xml" ); 511 if( parsererrorNS!= null && xmlDoc.getElementsByTagNameNS(parsererrorNS, "parsererror").length > 0) { 512 //throw new Error('Error parsing XML: '+xmlDocStr); 513 xmlDoc = null; 514 } 515 } 516 catch(err) { 517 xmlDoc = null; 518 } 519 } 520 else { 521 // IE :( 522 if(xmlDocStr.indexOf("<?")==0) { 523 xmlDocStr = xmlDocStr.substr( xmlDocStr.indexOf("?>") + 2 ); 524 } 525 xmlDoc=new ActiveXObject("Microsoft.XMLDOM"); 526 xmlDoc.async="false"; 527 xmlDoc.loadXML(xmlDocStr); 528 } 529 return xmlDoc; 530 }; 531 532 this.asArray = function(prop) { 533 if (prop === undefined || prop == null) 534 return []; 535 else 536 if(prop instanceof Array) 537 return prop; 538 else 539 return [prop]; 540 }; 541 542 this.toXmlDateTime = function(dt) { 543 if(dt instanceof Date) 544 return dt.toISOString(); 545 else 546 if(typeof(dt) === 'number' ) 547 return new Date(dt).toISOString(); 548 else 549 return null; 550 }; 551 552 this.asDateTime = function(prop) { 553 if(typeof(prop) == "string") { 554 return fromXmlDateTime(prop); 555 } 556 else 557 return prop; 558 }; 559 560 this.xml2json = function (xmlDoc) { 561 return parseDOMChildren ( xmlDoc ); 562 }; 563 564 this.xml_str2json = function (xmlDocStr) { 565 var xmlDoc = this.parseXmlString(xmlDocStr); 566 if(xmlDoc!=null) 567 return this.xml2json(xmlDoc); 568 else 569 return null; 570 }; 571 572 this.json2xml_str = function (jsonObj) { 573 return parseJSONObject ( jsonObj, "" ); 574 }; 575 576 this.json2xml = function (jsonObj) { 577 var xmlDocStr = this.json2xml_str (jsonObj); 578 return this.parseXmlString(xmlDocStr); 579 }; 580 581 this.getVersion = function () { 582 return VERSION; 583 }; 584 } 585}))
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.