|
1 /* |
|
2 http://www.JSON.org/json_parse.js |
|
3 2011-03-06 |
|
4 |
|
5 Public Domain. |
|
6 |
|
7 NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. |
|
8 |
|
9 This file creates a json_parse function. |
|
10 |
|
11 json_parse(text, reviver) |
|
12 This method parses a JSON text to produce an object or array. |
|
13 It can throw a SyntaxError exception. |
|
14 |
|
15 The optional reviver parameter is a function that can filter and |
|
16 transform the results. It receives each of the keys and values, |
|
17 and its return value is used instead of the original value. |
|
18 If it returns what it received, then the structure is not modified. |
|
19 If it returns undefined then the member is deleted. |
|
20 |
|
21 Example: |
|
22 |
|
23 // Parse the text. Values that look like ISO date strings will |
|
24 // be converted to Date objects. |
|
25 |
|
26 myData = json_parse(text, function (key, value) { |
|
27 var a; |
|
28 if (typeof value === 'string') { |
|
29 a = |
|
30 /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value); |
|
31 if (a) { |
|
32 return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], |
|
33 +a[5], +a[6])); |
|
34 } |
|
35 } |
|
36 return value; |
|
37 }); |
|
38 |
|
39 This is a reference implementation. You are free to copy, modify, or |
|
40 redistribute. |
|
41 |
|
42 This code should be minified before deployment. |
|
43 See http://javascript.crockford.com/jsmin.html |
|
44 |
|
45 USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO |
|
46 NOT CONTROL. |
|
47 */ |
|
48 |
|
49 /*members "", "\"", "\/", "\\", at, b, call, charAt, f, fromCharCode, |
|
50 hasOwnProperty, message, n, name, prototype, push, r, t, text |
|
51 */ |
|
52 |
|
53 var json_parse = (function () { |
|
54 "use strict"; |
|
55 |
|
56 // This is a function that can parse a JSON text, producing a JavaScript |
|
57 // data structure. It is a simple, recursive descent parser. It does not use |
|
58 // eval or regular expressions, so it can be used as a model for implementing |
|
59 // a JSON parser in other languages. |
|
60 |
|
61 // We are defining the function inside of another function to avoid creating |
|
62 // global variables. |
|
63 |
|
64 var at, // The index of the current character |
|
65 ch, // The current character |
|
66 escapee = { |
|
67 '"': '"', |
|
68 '\\': '\\', |
|
69 '/': '/', |
|
70 b: '\b', |
|
71 f: '\f', |
|
72 n: '\n', |
|
73 r: '\r', |
|
74 t: '\t' |
|
75 }, |
|
76 text, |
|
77 |
|
78 error = function (m) { |
|
79 |
|
80 // Call error when something is wrong. |
|
81 |
|
82 throw { |
|
83 name: 'SyntaxError', |
|
84 message: m, |
|
85 at: at, |
|
86 text: text |
|
87 }; |
|
88 }, |
|
89 |
|
90 next = function (c) { |
|
91 |
|
92 // If a c parameter is provided, verify that it matches the current character. |
|
93 |
|
94 if (c && c !== ch) { |
|
95 error("Expected '" + c + "' instead of '" + ch + "'"); |
|
96 } |
|
97 |
|
98 // Get the next character. When there are no more characters, |
|
99 // return the empty string. |
|
100 |
|
101 ch = text.charAt(at); |
|
102 at += 1; |
|
103 return ch; |
|
104 }, |
|
105 |
|
106 number = function () { |
|
107 |
|
108 // Parse a number value. |
|
109 |
|
110 var number, |
|
111 string = ''; |
|
112 |
|
113 if (ch === '-') { |
|
114 string = '-'; |
|
115 next('-'); |
|
116 } |
|
117 while (ch >= '0' && ch <= '9') { |
|
118 string += ch; |
|
119 next(); |
|
120 } |
|
121 if (ch === '.') { |
|
122 string += '.'; |
|
123 while (next() && ch >= '0' && ch <= '9') { |
|
124 string += ch; |
|
125 } |
|
126 } |
|
127 if (ch === 'e' || ch === 'E') { |
|
128 string += ch; |
|
129 next(); |
|
130 if (ch === '-' || ch === '+') { |
|
131 string += ch; |
|
132 next(); |
|
133 } |
|
134 while (ch >= '0' && ch <= '9') { |
|
135 string += ch; |
|
136 next(); |
|
137 } |
|
138 } |
|
139 number = +string; |
|
140 if (!isFinite(number)) { |
|
141 error("Bad number"); |
|
142 } else { |
|
143 return number; |
|
144 } |
|
145 }, |
|
146 |
|
147 string = function () { |
|
148 |
|
149 // Parse a string value. |
|
150 |
|
151 var hex, |
|
152 i, |
|
153 string = '', |
|
154 uffff; |
|
155 |
|
156 // When parsing for string values, we must look for " and \ characters. |
|
157 |
|
158 if (ch === '"') { |
|
159 while (next()) { |
|
160 if (ch === '"') { |
|
161 next(); |
|
162 return string; |
|
163 } else if (ch === '\\') { |
|
164 next(); |
|
165 if (ch === 'u') { |
|
166 uffff = 0; |
|
167 for (i = 0; i < 4; i += 1) { |
|
168 hex = parseInt(next(), 16); |
|
169 if (!isFinite(hex)) { |
|
170 break; |
|
171 } |
|
172 uffff = uffff * 16 + hex; |
|
173 } |
|
174 string += String.fromCharCode(uffff); |
|
175 } else if (typeof escapee[ch] === 'string') { |
|
176 string += escapee[ch]; |
|
177 } else { |
|
178 break; |
|
179 } |
|
180 } else { |
|
181 string += ch; |
|
182 } |
|
183 } |
|
184 } |
|
185 error("Bad string"); |
|
186 }, |
|
187 |
|
188 white = function () { |
|
189 |
|
190 // Skip whitespace. |
|
191 |
|
192 while (ch && ch <= ' ') { |
|
193 next(); |
|
194 } |
|
195 }, |
|
196 |
|
197 word = function () { |
|
198 |
|
199 // true, false, or null. |
|
200 |
|
201 switch (ch) { |
|
202 case 't': |
|
203 next('t'); |
|
204 next('r'); |
|
205 next('u'); |
|
206 next('e'); |
|
207 return true; |
|
208 case 'f': |
|
209 next('f'); |
|
210 next('a'); |
|
211 next('l'); |
|
212 next('s'); |
|
213 next('e'); |
|
214 return false; |
|
215 case 'n': |
|
216 next('n'); |
|
217 next('u'); |
|
218 next('l'); |
|
219 next('l'); |
|
220 return null; |
|
221 } |
|
222 error("Unexpected '" + ch + "'"); |
|
223 }, |
|
224 |
|
225 value, // Place holder for the value function. |
|
226 |
|
227 array = function () { |
|
228 |
|
229 // Parse an array value. |
|
230 |
|
231 var array = []; |
|
232 |
|
233 if (ch === '[') { |
|
234 next('['); |
|
235 white(); |
|
236 if (ch === ']') { |
|
237 next(']'); |
|
238 return array; // empty array |
|
239 } |
|
240 while (ch) { |
|
241 array.push(value()); |
|
242 white(); |
|
243 if (ch === ']') { |
|
244 next(']'); |
|
245 return array; |
|
246 } |
|
247 next(','); |
|
248 white(); |
|
249 } |
|
250 } |
|
251 error("Bad array"); |
|
252 }, |
|
253 |
|
254 object = function () { |
|
255 |
|
256 // Parse an object value. |
|
257 |
|
258 var key, |
|
259 object = {}; |
|
260 |
|
261 if (ch === '{') { |
|
262 next('{'); |
|
263 white(); |
|
264 if (ch === '}') { |
|
265 next('}'); |
|
266 return object; // empty object |
|
267 } |
|
268 while (ch) { |
|
269 key = string(); |
|
270 white(); |
|
271 next(':'); |
|
272 if (Object.hasOwnProperty.call(object, key)) { |
|
273 error('Duplicate key "' + key + '"'); |
|
274 } |
|
275 object[key] = value(); |
|
276 white(); |
|
277 if (ch === '}') { |
|
278 next('}'); |
|
279 return object; |
|
280 } |
|
281 next(','); |
|
282 white(); |
|
283 } |
|
284 } |
|
285 error("Bad object"); |
|
286 }; |
|
287 |
|
288 value = function () { |
|
289 |
|
290 // Parse a JSON value. It could be an object, an array, a string, a number, |
|
291 // or a word. |
|
292 |
|
293 white(); |
|
294 switch (ch) { |
|
295 case '{': |
|
296 return object(); |
|
297 case '[': |
|
298 return array(); |
|
299 case '"': |
|
300 return string(); |
|
301 case '-': |
|
302 return number(); |
|
303 default: |
|
304 return ch >= '0' && ch <= '9' ? number() : word(); |
|
305 } |
|
306 }; |
|
307 |
|
308 // Return the json_parse function. It will have access to all of the above |
|
309 // functions and variables. |
|
310 |
|
311 return function (source, reviver) { |
|
312 var result; |
|
313 |
|
314 text = source; |
|
315 at = 0; |
|
316 ch = ' '; |
|
317 result = value(); |
|
318 white(); |
|
319 if (ch) { |
|
320 error("Syntax error"); |
|
321 } |
|
322 |
|
323 // If there is a reviver function, we recursively walk the new structure, |
|
324 // passing each name/value pair to the reviver function for possible |
|
325 // transformation, starting with a temporary root object that holds the result |
|
326 // in an empty key. If there is not a reviver function, we simply return the |
|
327 // result. |
|
328 |
|
329 return typeof reviver === 'function' ? (function walk(holder, key) { |
|
330 var k, v, value = holder[key]; |
|
331 if (value && typeof value === 'object') { |
|
332 for (k in value) { |
|
333 if (Object.prototype.hasOwnProperty.call(value, k)) { |
|
334 v = walk(value, k); |
|
335 if (v !== undefined) { |
|
336 value[k] = v; |
|
337 } else { |
|
338 delete value[k]; |
|
339 } |
|
340 } |
|
341 } |
|
342 } |
|
343 return reviver.call(holder, key, value); |
|
344 }({'': result}, '')) : result; |
|
345 }; |
|
346 }()); |