json2-rails 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,349 @@
1
+ /*
2
+ json_parse.js
3
+ 2012-06-20
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
+ }
164
+ if (ch === '\\') {
165
+ next();
166
+ if (ch === 'u') {
167
+ uffff = 0;
168
+ for (i = 0; i < 4; i += 1) {
169
+ hex = parseInt(next(), 16);
170
+ if (!isFinite(hex)) {
171
+ break;
172
+ }
173
+ uffff = uffff * 16 + hex;
174
+ }
175
+ string += String.fromCharCode(uffff);
176
+ } else if (typeof escapee[ch] === 'string') {
177
+ string += escapee[ch];
178
+ } else {
179
+ break;
180
+ }
181
+ } else {
182
+ string += ch;
183
+ }
184
+ }
185
+ }
186
+ error("Bad string");
187
+ },
188
+
189
+ white = function () {
190
+
191
+ // Skip whitespace.
192
+
193
+ while (ch && ch <= ' ') {
194
+ next();
195
+ }
196
+ },
197
+
198
+ word = function () {
199
+
200
+ // true, false, or null.
201
+
202
+ switch (ch) {
203
+ case 't':
204
+ next('t');
205
+ next('r');
206
+ next('u');
207
+ next('e');
208
+ return true;
209
+ case 'f':
210
+ next('f');
211
+ next('a');
212
+ next('l');
213
+ next('s');
214
+ next('e');
215
+ return false;
216
+ case 'n':
217
+ next('n');
218
+ next('u');
219
+ next('l');
220
+ next('l');
221
+ return null;
222
+ }
223
+ error("Unexpected '" + ch + "'");
224
+ },
225
+
226
+ value, // Place holder for the value function.
227
+
228
+ array = function () {
229
+
230
+ // Parse an array value.
231
+
232
+ var array = [];
233
+
234
+ if (ch === '[') {
235
+ next('[');
236
+ white();
237
+ if (ch === ']') {
238
+ next(']');
239
+ return array; // empty array
240
+ }
241
+ while (ch) {
242
+ array.push(value());
243
+ white();
244
+ if (ch === ']') {
245
+ next(']');
246
+ return array;
247
+ }
248
+ next(',');
249
+ white();
250
+ }
251
+ }
252
+ error("Bad array");
253
+ },
254
+
255
+ object = function () {
256
+
257
+ // Parse an object value.
258
+
259
+ var key,
260
+ object = {};
261
+
262
+ if (ch === '{') {
263
+ next('{');
264
+ white();
265
+ if (ch === '}') {
266
+ next('}');
267
+ return object; // empty object
268
+ }
269
+ while (ch) {
270
+ key = string();
271
+ white();
272
+ next(':');
273
+ if (Object.hasOwnProperty.call(object, key)) {
274
+ error('Duplicate key "' + key + '"');
275
+ }
276
+ object[key] = value();
277
+ white();
278
+ if (ch === '}') {
279
+ next('}');
280
+ return object;
281
+ }
282
+ next(',');
283
+ white();
284
+ }
285
+ }
286
+ error("Bad object");
287
+ };
288
+
289
+ value = function () {
290
+
291
+ // Parse a JSON value. It could be an object, an array, a string, a number,
292
+ // or a word.
293
+
294
+ white();
295
+ switch (ch) {
296
+ case '{':
297
+ return object();
298
+ case '[':
299
+ return array();
300
+ case '"':
301
+ return string();
302
+ case '-':
303
+ return number();
304
+ default:
305
+ return ch >= '0' && ch <= '9' ? number() : word();
306
+ }
307
+ };
308
+
309
+ // Return the json_parse function. It will have access to all of the above
310
+ // functions and variables.
311
+
312
+ return function (source, reviver) {
313
+ var result;
314
+
315
+ text = source;
316
+ at = 0;
317
+ ch = ' ';
318
+ result = value();
319
+ white();
320
+ if (ch) {
321
+ error("Syntax error");
322
+ }
323
+
324
+ // If there is a reviver function, we recursively walk the new structure,
325
+ // passing each name/value pair to the reviver function for possible
326
+ // transformation, starting with a temporary root object that holds the result
327
+ // in an empty key. If there is not a reviver function, we simply return the
328
+ // result.
329
+
330
+ return typeof reviver === 'function'
331
+ ? (function walk(holder, key) {
332
+ var k, v, value = holder[key];
333
+ if (value && typeof value === 'object') {
334
+ for (k in value) {
335
+ if (Object.prototype.hasOwnProperty.call(value, k)) {
336
+ v = walk(value, k);
337
+ if (v !== undefined) {
338
+ value[k] = v;
339
+ } else {
340
+ delete value[k];
341
+ }
342
+ }
343
+ }
344
+ }
345
+ return reviver.call(holder, key, value);
346
+ }({'': result}, ''))
347
+ : result;
348
+ };
349
+ }());
@@ -0,0 +1,397 @@
1
+ /*
2
+ json_parse_state.js
3
+ 2012-06-01
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
+ /*jslint regexp: false*/
50
+
51
+ /*members "", "\"", ",", "\/", ":", "[", "\\", "]", acomma, avalue, b,
52
+ call, colon, container, exec, f, false, firstavalue, firstokey,
53
+ fromCharCode, go, hasOwnProperty, key, length, n, null, ocomma, okey,
54
+ ovalue, pop, prototype, push, r, replace, slice, state, t, test, true,
55
+ value, "{", "}"
56
+ */
57
+
58
+ var json_parse = (function () {
59
+ "use strict";
60
+
61
+ // This function creates a JSON parse function that uses a state machine rather
62
+ // than the dangerous eval function to parse a JSON text.
63
+
64
+ var state, // The state of the parser, one of
65
+ // 'go' The starting state
66
+ // 'ok' The final, accepting state
67
+ // 'firstokey' Ready for the first key of the object or
68
+ // the closing of an empty object
69
+ // 'okey' Ready for the next key of the object
70
+ // 'colon' Ready for the colon
71
+ // 'ovalue' Ready for the value half of a key/value pair
72
+ // 'ocomma' Ready for a comma or closing }
73
+ // 'firstavalue' Ready for the first value of an array or
74
+ // an empty array
75
+ // 'avalue' Ready for the next value of an array
76
+ // 'acomma' Ready for a comma or closing ]
77
+ stack, // The stack, for controlling nesting.
78
+ container, // The current container object or array
79
+ key, // The current key
80
+ value, // The current value
81
+ escapes = { // Escapement translation table
82
+ '\\': '\\',
83
+ '"': '"',
84
+ '/': '/',
85
+ 't': '\t',
86
+ 'n': '\n',
87
+ 'r': '\r',
88
+ 'f': '\f',
89
+ 'b': '\b'
90
+ },
91
+ string = { // The actions for string tokens
92
+ go: function () {
93
+ state = 'ok';
94
+ },
95
+ firstokey: function () {
96
+ key = value;
97
+ state = 'colon';
98
+ },
99
+ okey: function () {
100
+ key = value;
101
+ state = 'colon';
102
+ },
103
+ ovalue: function () {
104
+ state = 'ocomma';
105
+ },
106
+ firstavalue: function () {
107
+ state = 'acomma';
108
+ },
109
+ avalue: function () {
110
+ state = 'acomma';
111
+ }
112
+ },
113
+ number = { // The actions for number tokens
114
+ go: function () {
115
+ state = 'ok';
116
+ },
117
+ ovalue: function () {
118
+ state = 'ocomma';
119
+ },
120
+ firstavalue: function () {
121
+ state = 'acomma';
122
+ },
123
+ avalue: function () {
124
+ state = 'acomma';
125
+ }
126
+ },
127
+ action = {
128
+
129
+ // The action table describes the behavior of the machine. It contains an
130
+ // object for each token. Each object contains a method that is called when
131
+ // a token is matched in a state. An object will lack a method for illegal
132
+ // states.
133
+
134
+ '{': {
135
+ go: function () {
136
+ stack.push({state: 'ok'});
137
+ container = {};
138
+ state = 'firstokey';
139
+ },
140
+ ovalue: function () {
141
+ stack.push({container: container, state: 'ocomma', key: key});
142
+ container = {};
143
+ state = 'firstokey';
144
+ },
145
+ firstavalue: function () {
146
+ stack.push({container: container, state: 'acomma'});
147
+ container = {};
148
+ state = 'firstokey';
149
+ },
150
+ avalue: function () {
151
+ stack.push({container: container, state: 'acomma'});
152
+ container = {};
153
+ state = 'firstokey';
154
+ }
155
+ },
156
+ '}': {
157
+ firstokey: function () {
158
+ var pop = stack.pop();
159
+ value = container;
160
+ container = pop.container;
161
+ key = pop.key;
162
+ state = pop.state;
163
+ },
164
+ ocomma: function () {
165
+ var pop = stack.pop();
166
+ container[key] = value;
167
+ value = container;
168
+ container = pop.container;
169
+ key = pop.key;
170
+ state = pop.state;
171
+ }
172
+ },
173
+ '[': {
174
+ go: function () {
175
+ stack.push({state: 'ok'});
176
+ container = [];
177
+ state = 'firstavalue';
178
+ },
179
+ ovalue: function () {
180
+ stack.push({container: container, state: 'ocomma', key: key});
181
+ container = [];
182
+ state = 'firstavalue';
183
+ },
184
+ firstavalue: function () {
185
+ stack.push({container: container, state: 'acomma'});
186
+ container = [];
187
+ state = 'firstavalue';
188
+ },
189
+ avalue: function () {
190
+ stack.push({container: container, state: 'acomma'});
191
+ container = [];
192
+ state = 'firstavalue';
193
+ }
194
+ },
195
+ ']': {
196
+ firstavalue: function () {
197
+ var pop = stack.pop();
198
+ value = container;
199
+ container = pop.container;
200
+ key = pop.key;
201
+ state = pop.state;
202
+ },
203
+ acomma: function () {
204
+ var pop = stack.pop();
205
+ container.push(value);
206
+ value = container;
207
+ container = pop.container;
208
+ key = pop.key;
209
+ state = pop.state;
210
+ }
211
+ },
212
+ ':': {
213
+ colon: function () {
214
+ if (Object.hasOwnProperty.call(container, key)) {
215
+ throw new SyntaxError('Duplicate key "' + key + '"');
216
+ }
217
+ state = 'ovalue';
218
+ }
219
+ },
220
+ ',': {
221
+ ocomma: function () {
222
+ container[key] = value;
223
+ state = 'okey';
224
+ },
225
+ acomma: function () {
226
+ container.push(value);
227
+ state = 'avalue';
228
+ }
229
+ },
230
+ 'true': {
231
+ go: function () {
232
+ value = true;
233
+ state = 'ok';
234
+ },
235
+ ovalue: function () {
236
+ value = true;
237
+ state = 'ocomma';
238
+ },
239
+ firstavalue: function () {
240
+ value = true;
241
+ state = 'acomma';
242
+ },
243
+ avalue: function () {
244
+ value = true;
245
+ state = 'acomma';
246
+ }
247
+ },
248
+ 'false': {
249
+ go: function () {
250
+ value = false;
251
+ state = 'ok';
252
+ },
253
+ ovalue: function () {
254
+ value = false;
255
+ state = 'ocomma';
256
+ },
257
+ firstavalue: function () {
258
+ value = false;
259
+ state = 'acomma';
260
+ },
261
+ avalue: function () {
262
+ value = false;
263
+ state = 'acomma';
264
+ }
265
+ },
266
+ 'null': {
267
+ go: function () {
268
+ value = null;
269
+ state = 'ok';
270
+ },
271
+ ovalue: function () {
272
+ value = null;
273
+ state = 'ocomma';
274
+ },
275
+ firstavalue: function () {
276
+ value = null;
277
+ state = 'acomma';
278
+ },
279
+ avalue: function () {
280
+ value = null;
281
+ state = 'acomma';
282
+ }
283
+ }
284
+ };
285
+
286
+ function debackslashify(text) {
287
+
288
+ // Remove and replace any backslash escapement.
289
+
290
+ return text.replace(/\\(?:u(.{4})|([^u]))/g, function (a, b, c) {
291
+ return b ? String.fromCharCode(parseInt(b, 16)) : escapes[c];
292
+ });
293
+ }
294
+
295
+ return function (source, reviver) {
296
+
297
+ // A regular expression is used to extract tokens from the JSON text.
298
+ // The extraction process is cautious.
299
+
300
+ var r, // The result of the exec method.
301
+ tx = /^[\x20\t\n\r]*(?:([,:\[\]{}]|true|false|null)|(-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)|"((?:[^\r\n\t\\\"]|\\(?:["\\\/trnfb]|u[0-9a-fA-F]{4}))*)")/;
302
+
303
+ // Set the starting state.
304
+
305
+ state = 'go';
306
+
307
+ // The stack records the container, key, and state for each object or array
308
+ // that contains another object or array while processing nested structures.
309
+
310
+ stack = [];
311
+
312
+ // If any error occurs, we will catch it and ultimately throw a syntax error.
313
+
314
+ try {
315
+
316
+ // For each token...
317
+
318
+ for (;;) {
319
+ r = tx.exec(source);
320
+ if (!r) {
321
+ break;
322
+ }
323
+
324
+ // r is the result array from matching the tokenizing regular expression.
325
+ // r[0] contains everything that matched, including any initial whitespace.
326
+ // r[1] contains any punctuation that was matched, or true, false, or null.
327
+ // r[2] contains a matched number, still in string form.
328
+ // r[3] contains a matched string, without quotes but with escapement.
329
+
330
+ if (r[1]) {
331
+
332
+ // Token: Execute the action for this state and token.
333
+
334
+ action[r[1]][state]();
335
+
336
+ } else if (r[2]) {
337
+
338
+ // Number token: Convert the number string into a number value and execute
339
+ // the action for this state and number.
340
+
341
+ value = +r[2];
342
+ number[state]();
343
+ } else {
344
+
345
+ // String token: Replace the escapement sequences and execute the action for
346
+ // this state and string.
347
+
348
+ value = debackslashify(r[3]);
349
+ string[state]();
350
+ }
351
+
352
+ // Remove the token from the string. The loop will continue as long as there
353
+ // are tokens. This is a slow process, but it allows the use of ^ matching,
354
+ // which assures that no illegal tokens slip through.
355
+
356
+ source = source.slice(r[0].length);
357
+ }
358
+
359
+ // If we find a state/token combination that is illegal, then the action will
360
+ // cause an error. We handle the error by simply changing the state.
361
+
362
+ } catch (e) {
363
+ state = e;
364
+ }
365
+
366
+ // The parsing is finished. If we are not in the final 'ok' state, or if the
367
+ // remaining source contains anything except whitespace, then we did not have
368
+ //a well-formed JSON text.
369
+
370
+ if (state !== 'ok' || /[^\x20\t\n\r]/.test(source)) {
371
+ throw state instanceof SyntaxError ? state : new SyntaxError('JSON');
372
+ }
373
+
374
+ // If there is a reviver function, we recursively walk the new structure,
375
+ // passing each name/value pair to the reviver function for possible
376
+ // transformation, starting with a temporary root object that holds the current
377
+ // value in an empty key. If there is not a reviver function, we simply return
378
+ // that value.
379
+
380
+ return typeof reviver === 'function' ? (function walk(holder, key) {
381
+ var k, v, value = holder[key];
382
+ if (value && typeof value === 'object') {
383
+ for (k in value) {
384
+ if (Object.prototype.hasOwnProperty.call(value, k)) {
385
+ v = walk(value, k);
386
+ if (v !== undefined) {
387
+ value[k] = v;
388
+ } else {
389
+ delete value[k];
390
+ }
391
+ }
392
+ }
393
+ }
394
+ return reviver.call(holder, key, value);
395
+ }({'': value}, '')) : value;
396
+ };
397
+ }());