-
Notifications
You must be signed in to change notification settings - Fork 18
/
jquery.xml.js
169 lines (161 loc) · 6.97 KB
/
jquery.xml.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
/**
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* Copyright (c) 2012 Sam Tsvilik
* Licensed under the MIT, GPL licenses.
*
* @name xml
* @version 1.1
* @author Sam Tsvilik
* @description
* This is a super light and simple XML to JSON converter.
* All it does is scans through child elements of your XML and builds out a JSON structure.
* To avoid attribute vs. node name conflicts - All attribute entities are prefixed with "@" (i.e. <node attr="1"/> == {node: {"@attr":"1"}} )
* Text or CDATA value will always be inside a "Text" property (i.e. myNodeObj.Text == <myNodeObj>Hello</myNodeObj> - Hello)
* Node siblings with the same name will be automatically converted into arrays, else if node is singular it will just be an Object
*/
(function(window, $, undef) {
var NULL = null,
FALSE = !1,
TRUE = !0,
NODE_TYPES = {
Element: 1,
Attribute: 2,
Text: 3,
CDATA: 4,
Root: 9,
Fragment: 11
},
XMLConverter, module;
/**
* Parses XML string and returns an XMLDocument object
* @param {String} strXML XML Formatted string
* @return {XMLDocument|XMLElement}
*/
function parseXMLString(strXML) {
var xmlDoc = NULL,
out = NULL,
isParsed = TRUE;
try {
xmlDoc = ("DOMParser" in window) ? new DOMParser() : new ActiveXObject("MSXML2.DOMDocument");
xmlDoc.async = FALSE;
} catch(e) {
throw new Error("XML Parser could not be instantiated");
}
if("parseFromString" in xmlDoc) {
out = xmlDoc.parseFromString(strXML, "text/xml");
isParsed = (out.documentElement.tagName !== "parsererror");
} else { //If old IE
isParsed = xmlDoc.loadXML(strXML);
out = (isParsed) ? xmlDoc : FALSE;
}
if(!isParsed) {
throw new Error("Error parsing XML string");
}
return out;
}
XMLConverter = {
isUnsafe: FALSE,
isXML: function(o) {
return(typeof(o) === "object" && o.nodeType !== undef);
},
getRoot: function(doc) {
return(doc.nodeType === NODE_TYPES.Root) ? doc.documentElement : (doc.nodeType === NODE_TYPES.Fragment) ? doc.firstChild : doc;
},
/**
* Begins the conversion process. Will automatically convert XML string into XMLDocument
* @param {String|XMLDocument|XMLNode|XMLElement} xml XML you want to convert to JSON
* @return {JSON} JSON object representing the XML data tree
*/
convert: function(xml) {
var out = {},
xdoc = typeof(xml) === "string" ? parseXMLString(xml) : this.isXML(xml) ? xml : undef,
root;
if(!xdoc) {
throw new Error("Unable to parse XML");
}
//If xdoc is just a text or CDATA return value
if(xdoc.nodeType === NODE_TYPES.Text || xdoc.nodeType === NODE_TYPES.CDATA) {
return xdoc.nodeValue;
}
//Extract root node
root = this.getRoot(xdoc);
//Create first root node
out[root.nodeName] = {};
//Start assembling the JSON tree (recursive)
this.process(root, out[root.nodeName]);
//Parse JSON string and attempt to return it as an Object
return out;
},
/**
* Recursive xmlNode processor. It determines the node type and processes it accordingly.
* @param {XMLNode} node Any XML node
* @param {Object} buff Buffer object which will contain the JSON equivalent properties
*/
process: function(node, buff) {
var child, attr, name, att_name, value, i, j, tmp, iMax;
if(node.hasChildNodes()) {
iMax = node.childNodes.length;
for(i = 0; i < iMax; i++) {
child = node.childNodes[i];
//Check nodeType of each child node
switch(child.nodeType) {
case NODE_TYPES.Text:
//If parent node has both CDATA and Text nodes, we just concatinate them together
buff.Text = buff.Text ? buff.Text + $.trim(child.nodeValue) : $.trim(child.nodeValue);
break;
case NODE_TYPES.CDATA:
//If parent node has both CDATA and Text nodes, we just concatinate them together
value = child[child.text ? "text" : "nodeValue"]; //IE attributes support
buff.Text = buff.Text ? buff.Text + value : value;
break;
case NODE_TYPES.Element:
name = child.nodeName;
tmp = {};
//Node name already exists in the buffer and it's a NodeSet
if(name in buff) {
if(buff[name].length) {
this.process(child, tmp);
buff[name].push(tmp);
} else { //If node exists in the parent as a single entity
this.process(child, tmp);
buff[name] = [buff[name], tmp];
}
} else { //If node does not exist in the parent
this.process(child, tmp);
buff[name] = tmp;
}
break;
}
}
}
//Populate attributes
if(node.attributes.length) {
for(j = node.attributes.length - 1; j >= 0; j--) {
attr = node.attributes[j];
att_name = $.trim(attr.name);
value = attr.value;
buff[(this.isUnsafe ? "" : "@") + att_name] = value;
}
}
}
};
/**
* Convert XML string or document to JSON
* @param {String|XMLDocument|XMLElement|XMLNode} xml Any type of XML
* @param {Boolean} unsafe Allows unsafe processing that does not prefixes attributes with '@' character. It is considered unsafe bacause attribute names may collide with node names.
* @return {Object} JSON equivalent of the XML source
*/
$.xmlToJSON = function(xml, unsafe) {
XMLConverter.isUnsafe = (unsafe !== undef) ? unsafe : FALSE;
return XMLConverter.convert(xml);
};
}(window, jQuery));