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