1/* Copyright 2015 William Summers, MetaTribal LLC 2 * adapted from https://developer.mozilla.org/en-US/docs/JXON 3 * 4 * Licensed under the MIT License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * https://opensource.org/licenses/MIT 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16/** 17 * @author William Summers 18 * 19 */ 20 21var xmlToJSON = (function () { 22 23 this.version = "1.3"; 24 25 var options = { // set up the default options 26 mergeCDATA: true, // extract cdata and merge with text 27 grokAttr: true, // convert truthy attributes to boolean, etc 28 grokText: true, // convert truthy text/attr to boolean, etc 29 normalize: true, // collapse multiple spaces to single space 30 xmlns: true, // include namespaces as attribute in output 31 namespaceKey: '_ns', // tag name for namespace objects 32 textKey: '_text', // tag name for text nodes 33 valueKey: '_value', // tag name for attribute values 34 attrKey: '_attr', // tag for attr groups 35 cdataKey: '_cdata', // tag for cdata nodes (ignored if mergeCDATA is true) 36 attrsAsObject: true, // if false, key is used as prefix to name, set prefix to '' to merge children and attrs. 37 stripAttrPrefix: true, // remove namespace prefixes from attributes 38 stripElemPrefix: true, // for elements of same name in diff namespaces, you can enable namespaces and access the nskey property 39 childrenAsArray: true // force children into arrays 40 }; 41 42 var prefixMatch = new RegExp(/(?!xmlns)^.*:/); 43 var trimMatch = new RegExp(/^\s+|\s+$/g); 44 45 this.grokType = function (sValue) { 46 if (/^\s*$/.test(sValue)) { 47 return null; 48 } 49 if (/^(?:true|false)$/i.test(sValue)) { 50 return sValue.toLowerCase() === "true"; 51 } 52 if (isFinite(sValue)) { 53 return parseFloat(sValue); 54 } 55 return sValue; 56 }; 57 58 this.parseString = function (xmlString, opt) { 59 return this.parseXML(this.stringToXML(xmlString), opt); 60 } 61 62 this.parseXML = function (oXMLParent, opt) { 63 64 // initialize options 65 for (var key in opt) { 66 options[key] = opt[key]; 67 } 68 69 var vResult = {}, 70 nLength = 0, 71 sCollectedTxt = ""; 72 73 // parse namespace information 74 if (options.xmlns && oXMLParent.namespaceURI) { 75 vResult[options.namespaceKey] = oXMLParent.namespaceURI; 76 } 77 78 // parse attributes 79 // using attributes property instead of hasAttributes method to support older browsers 80 if (oXMLParent.attributes && oXMLParent.attributes.length > 0) { 81 var vAttribs = {}; 82 83 for (nLength; nLength < oXMLParent.attributes.length; nLength++) { 84 var oAttrib = oXMLParent.attributes.item(nLength); 85 vContent = {}; 86 var attribName = ''; 87 88 if (options.stripAttrPrefix) { 89 attribName = oAttrib.name.replace(prefixMatch, ''); 90 91 } else { 92 attribName = oAttrib.name; 93 } 94 95 if (options.grokAttr) { 96 vContent[options.valueKey] = this.grokType(oAttrib.value.replace(trimMatch, '')); 97 } else { 98 vContent[options.valueKey] = oAttrib.value.replace(trimMatch, ''); 99 } 100 101 if (options.xmlns && oAttrib.namespaceURI) { 102 vContent[options.namespaceKey] = oAttrib.namespaceURI; 103 } 104 105 if (options.attrsAsObject) { // attributes with same local name must enable prefixes 106 vAttribs[attribName] = vContent; 107 } else { 108 vResult[options.attrKey + attribName] = vContent; 109 } 110 } 111 112 if (options.attrsAsObject) { 113 vResult[options.attrKey] = vAttribs; 114 } else {} 115 } 116 117 // iterate over the children 118 if (oXMLParent.hasChildNodes()) { 119 for (var oNode, sProp, vContent, nItem = 0; nItem < oXMLParent.childNodes.length; nItem++) { 120 oNode = oXMLParent.childNodes.item(nItem); 121 122 if (oNode.nodeType === 4) { 123 if (options.mergeCDATA) { 124 sCollectedTxt += oNode.nodeValue; 125 } else { 126 if (vResult.hasOwnProperty(options.cdataKey)) { 127 if (vResult[options.cdataKey].constructor !== Array) { 128 vResult[options.cdataKey] = [vResult[options.cdataKey]]; 129 } 130 vResult[options.cdataKey].push(oNode.nodeValue); 131 132 } else { 133 if (options.childrenAsArray) { 134 vResult[options.cdataKey] = []; 135 vResult[options.cdataKey].push(oNode.nodeValue); 136 } else { 137 vResult[options.cdataKey] = oNode.nodeValue; 138 } 139 } 140 } 141 } /* nodeType is "CDATASection" (4) */ 142 else if (oNode.nodeType === 3) { 143 sCollectedTxt += oNode.nodeValue; 144 } /* nodeType is "Text" (3) */ 145 else if (oNode.nodeType === 1) { /* nodeType is "Element" (1) */ 146 147 if (nLength === 0) { 148 vResult = {}; 149 } 150
151 // using nodeName to support browser (IE) implementation with no 'localName' property 152 if (options.stripElemPrefix) { 153 sProp = oNode.nodeName.replace(prefixMatch, ''); 154 } else { 155 sProp = oNode.nodeName; 156 } 157 158 vContent = xmlToJSON.parseXML(oNode); 159 160 if (vResult.hasOwnProperty(sProp)) { 161 if (vResult[sProp].constructor !== Array) { 162 vResult[sProp] = [vResult[sProp]]; 163 } 164 vResult[sProp].push(vContent); 165 166 } else { 167 if (options.childrenAsArray) { 168 vResult[sProp] = []; 169 vResult[sProp].push(vContent); 170 } else { 171 vResult[sProp] = vContent; 172 } 173 nLength++; 174 } 175 } 176 } 177 } else if (!sCollectedTxt) { // no children and no text, return null 178 if (options.childrenAsArray) { 179 vResult[options.textKey] = []; 180 vResult[options.textKey].push(null); 181 } else { 182 vResult[options.textKey] = null; 183 } 184 } 185 186 if (sCollectedTxt) { 187 if (options.grokText) { 188 var value = this.grokType(sCollectedTxt.replace(trimMatch, '')); 189 if (value !== null && value !== undefined) { 190 vResult[options.textKey] = value; 191 } 192 } else if (options.normalize) { 193 vResult[options.textKey] = sCollectedTxt.replace(trimMatch, '').replace(/\s+/g, " "); 194 } else { 195 vResult[options.textKey] = sCollectedTxt.replace(trimMatch, ''); 196 } 197 } 198 199 return vResult; 200 } 201 202 203 // Convert xmlDocument to a string 204 // Returns null on failure 205 this.xmlToString = function (xmlDoc) { 206 try { 207 var xmlString = xmlDoc.xml ? xmlDoc.xml : (new XMLSerializer()).serializeToString(xmlDoc); 208 return xmlString; 209 } catch (err) { 210 return null; 211 } 212 } 213 214 // Convert a string to XML Node Structure 215 // Returns null on failure 216 this.stringToXML = function (xmlString) { 217 try { 218 var xmlDoc = null; 219 220 if (window.DOMParser) { 221 222 var parser = new DOMParser(); 223 xmlDoc = parser.parseFromString(xmlString, "text/xml"); 224 225 return xmlDoc; 226 } else { 227 xmlDoc = new ActiveXObject("Microsoft.XMLDOM"); 228 xmlDoc.async = false; 229 xmlDoc.loadXML(xmlString); 230 231 return xmlDoc; 232 } 233 } catch (e) { 234 return null; 235 } 236 } 237 238 return this; 239}).call({}); 240 241if (typeof module != "undefined" && module !== null && module.exports) module.exports = xmlToJSON; 242else if (typeof define === "function" && define.amd) define(function() {return xmlToJSON});
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.