Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(507)

Side by Side Diff: pkg/json/lib/json.dart

Issue 225973004: Removed pkg/json from SDK sources (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « pkg/json/README.md ('k') | pkg/json/pubspec.yaml » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4
5 /**
6 * **DEPRECATED** Use the JSON facilities in `dart:convert` instead.
7 *
8 * Utilities for encoding and decoding JSON (JavaScript Object Notation) data.
9 */
10 @deprecated
11 library json;
12
13 import 'dart:convert' show JSON, StringConversionSink;
14 export 'dart:convert' show JsonUnsupportedObjectError, JsonCyclicError;
15
16 /**
17 * **DEPRECATED** Use [JSON.decode] from `dart:convert` instead.
18 *
19 * Parses [json] and build the corresponding parsed JSON value.
20 *
21 * Parsed JSON values are of the types [num], [String], [bool], [Null],
22 * [List]s of parsed JSON values or [Map]s from [String] to parsed
23 * JSON values.
24 *
25 * The optional [reviver] function, if provided, is called once for each
26 * object or list property parsed. The arguments are the property name
27 * ([String]) or list index ([int]), and the value is the parsed value.
28 * The return value of the reviver will be used as the value of that property
29 * instead the parsed value.
30 *
31 * Throws [FormatException] if the input is not valid JSON text.
32 */
33 @deprecated
34 parse(String json, [reviver(var key, var value)]) {
35 return JSON.decode(json, reviver: reviver);
36 }
37
38 /**
39 * **DEPRECATED**. Use [JsonCodec] from `dart:convert` instead.
40 *
41 * Serializes [object] into a JSON string.
42 *
43 * Directly serializable values are [num], [String], [bool], and [Null], as well
44 * as some [List] and [Map] values.
45 * For [List], the elements must all be serializable.
46 * For [Map], the keys must be [String] and the values must be serializable.
47 *
48 * If a value is any other type is attempted serialized, a "toJson()" method
49 * is invoked on the object and the result, which must be a directly
50 * serializable value, is serialized instead of the original value.
51 *
52 * If the object does not support this method, throws, or returns a
53 * value that is not directly serializable, a [JsonUnsupportedObjectError]
54 * exception is thrown. If the call throws (including the case where there
55 * is no nullary "toJson" method, the error is caught and stored in the
56 * [JsonUnsupportedObjectError]'s [:cause:] field.
57 *
58 * If a [List] or [Map] contains a reference to itself, directly or through
59 * other lists or maps, it cannot be serialized and a [JsonCyclicError] is
60 * thrown.
61 *
62 * Json Objects should not change during serialization.
63 * If an object is serialized more than once, [stringify] is allowed to cache
64 * the JSON text for it. I.e., if an object changes after it is first
65 * serialized, the new values may or may not be reflected in the result.
66 */
67 @deprecated
68 String stringify(Object object) {
69 return JSON.encode(object);
70 }
71
72 /**
73 * **DEPRECATED**. Use [JsonCodec] from `dart:convert` instead.
74 *
75 * Serializes [object] into [output] stream.
76 *
77 * Performs the same operations as [stringify] but outputs the resulting
78 * string to an existing [StringSink] instead of creating a new [String].
79 *
80 * If serialization fails by throwing, some data might have been added to
81 * [output], but it won't contain valid JSON text.
82 */
83 @deprecated
84 void printOn(Object object, StringSink output) {
85 var stringConversionSink = new StringConversionSink.fromStringSink(output);
86 JSON.encoder.startChunkedConversion(stringConversionSink).add(object);
87 }
88
89 //// Implementation ///////////////////////////////////////////////////////////
90
91 // Simple API for JSON parsing.
92
93 abstract class JsonListener {
94 void handleString(String value) {}
95 void handleNumber(num value) {}
96 void handleBool(bool value) {}
97 void handleNull() {}
98 void beginObject() {}
99 void propertyName() {}
100 void propertyValue() {}
101 void endObject() {}
102 void beginArray() {}
103 void arrayElement() {}
104 void endArray() {}
105 /** Called on failure to parse [source]. */
106 void fail(String source, int position, String message) {}
107 }
108
109 /**
110 * A [JsonListener] that builds data objects from the parser events.
111 *
112 * This is a simple stack-based object builder. It keeps the most recently
113 * seen value in a variable, and uses it depending on the following event.
114 */
115 class BuildJsonListener extends JsonListener {
116 /**
117 * Stack used to handle nested containers.
118 *
119 * The current container is pushed on the stack when a new one is
120 * started. If the container is a [Map], there is also a current [key]
121 * which is also stored on the stack.
122 */
123 List stack = [];
124 /** The current [Map] or [List] being built. */
125 var currentContainer;
126 /** The most recently read property key. */
127 String key;
128 /** The most recently read value. */
129 var value;
130
131 /** Pushes the currently active container (and key, if a [Map]). */
132 void pushContainer() {
133 if (currentContainer is Map) stack.add(key);
134 stack.add(currentContainer);
135 }
136
137 /** Pops the top container from the [stack], including a key if applicable. */
138 void popContainer() {
139 value = currentContainer;
140 currentContainer = stack.removeLast();
141 if (currentContainer is Map) key = stack.removeLast();
142 }
143
144 void handleString(String value) { this.value = value; }
145 void handleNumber(num value) { this.value = value; }
146 void handleBool(bool value) { this.value = value; }
147 void handleNull() { this.value = null; }
148
149 void beginObject() {
150 pushContainer();
151 currentContainer = {};
152 }
153
154 void propertyName() {
155 key = value;
156 value = null;
157 }
158
159 void propertyValue() {
160 Map map = currentContainer;
161 map[key] = value;
162 key = value = null;
163 }
164
165 void endObject() {
166 popContainer();
167 }
168
169 void beginArray() {
170 pushContainer();
171 currentContainer = [];
172 }
173
174 void arrayElement() {
175 List list = currentContainer;
176 currentContainer.add(value);
177 value = null;
178 }
179
180 void endArray() {
181 popContainer();
182 }
183
184 /** Read out the final result of parsing a JSON string. */
185 get result {
186 assert(currentContainer == null);
187 return value;
188 }
189 }
190
191 typedef _Reviver(var key, var value);
192
193 class ReviverJsonListener extends BuildJsonListener {
194 final _Reviver reviver;
195 ReviverJsonListener(reviver(key, value)) : this.reviver = reviver;
196
197 void arrayElement() {
198 List list = currentContainer;
199 value = reviver(list.length, value);
200 super.arrayElement();
201 }
202
203 void propertyValue() {
204 value = reviver(key, value);
205 super.propertyValue();
206 }
207
208 get result {
209 return reviver("", value);
210 }
211 }
212
213 class JsonParser {
214 // A simple non-recursive state-based parser for JSON.
215 //
216 // Literal values accepted in states ARRAY_EMPTY, ARRAY_COMMA, OBJECT_COLON
217 // and strings also in OBJECT_EMPTY, OBJECT_COMMA.
218 // VALUE STRING : , } ] Transitions to
219 // EMPTY X X -> END
220 // ARRAY_EMPTY X X @ -> ARRAY_VALUE / pop
221 // ARRAY_VALUE @ @ -> ARRAY_COMMA / pop
222 // ARRAY_COMMA X X -> ARRAY_VALUE
223 // OBJECT_EMPTY X @ -> OBJECT_KEY / pop
224 // OBJECT_KEY @ -> OBJECT_COLON
225 // OBJECT_COLON X X -> OBJECT_VALUE
226 // OBJECT_VALUE @ @ -> OBJECT_COMMA / pop
227 // OBJECT_COMMA X -> OBJECT_KEY
228 // END
229 // Starting a new array or object will push the current state. The "pop"
230 // above means restoring this state and then marking it as an ended value.
231 // X means generic handling, @ means special handling for just that
232 // state - that is, values are handled generically, only punctuation
233 // cares about the current state.
234 // Values for states are chosen so bits 0 and 1 tell whether
235 // a string/value is allowed, and setting bits 0 through 2 after a value
236 // gets to the next state (not empty, doesn't allow a value).
237
238 // State building-block constants.
239 static const int INSIDE_ARRAY = 1;
240 static const int INSIDE_OBJECT = 2;
241 static const int AFTER_COLON = 3; // Always inside object.
242
243 static const int ALLOW_STRING_MASK = 8; // Allowed if zero.
244 static const int ALLOW_VALUE_MASK = 4; // Allowed if zero.
245 static const int ALLOW_VALUE = 0;
246 static const int STRING_ONLY = 4;
247 static const int NO_VALUES = 12;
248
249 // Objects and arrays are "empty" until their first property/element.
250 static const int EMPTY = 0;
251 static const int NON_EMPTY = 16;
252 static const int EMPTY_MASK = 16; // Empty if zero.
253
254
255 static const int VALUE_READ_BITS = NO_VALUES | NON_EMPTY;
256
257 // Actual states.
258 static const int STATE_INITIAL = EMPTY | ALLOW_VALUE;
259 static const int STATE_END = NON_EMPTY | NO_VALUES;
260
261 static const int STATE_ARRAY_EMPTY = INSIDE_ARRAY | EMPTY | ALLOW_VALUE;
262 static const int STATE_ARRAY_VALUE = INSIDE_ARRAY | NON_EMPTY | NO_VALUES;
263 static const int STATE_ARRAY_COMMA = INSIDE_ARRAY | NON_EMPTY | ALLOW_VALUE;
264
265 static const int STATE_OBJECT_EMPTY = INSIDE_OBJECT | EMPTY | STRING_ONLY;
266 static const int STATE_OBJECT_KEY = INSIDE_OBJECT | NON_EMPTY | NO_VALUES;
267 static const int STATE_OBJECT_COLON = AFTER_COLON | NON_EMPTY | ALLOW_VALUE;
268 static const int STATE_OBJECT_VALUE = AFTER_COLON | NON_EMPTY | NO_VALUES;
269 static const int STATE_OBJECT_COMMA = INSIDE_OBJECT | NON_EMPTY | STRING_ONLY;
270
271 // Character code constants.
272 static const int BACKSPACE = 0x08;
273 static const int TAB = 0x09;
274 static const int NEWLINE = 0x0a;
275 static const int CARRIAGE_RETURN = 0x0d;
276 static const int FORM_FEED = 0x0c;
277 static const int SPACE = 0x20;
278 static const int QUOTE = 0x22;
279 static const int PLUS = 0x2b;
280 static const int COMMA = 0x2c;
281 static const int MINUS = 0x2d;
282 static const int DECIMALPOINT = 0x2e;
283 static const int SLASH = 0x2f;
284 static const int CHAR_0 = 0x30;
285 static const int CHAR_9 = 0x39;
286 static const int COLON = 0x3a;
287 static const int CHAR_E = 0x45;
288 static const int LBRACKET = 0x5b;
289 static const int BACKSLASH = 0x5c;
290 static const int RBRACKET = 0x5d;
291 static const int CHAR_a = 0x61;
292 static const int CHAR_b = 0x62;
293 static const int CHAR_e = 0x65;
294 static const int CHAR_f = 0x66;
295 static const int CHAR_l = 0x6c;
296 static const int CHAR_n = 0x6e;
297 static const int CHAR_r = 0x72;
298 static const int CHAR_s = 0x73;
299 static const int CHAR_t = 0x74;
300 static const int CHAR_u = 0x75;
301 static const int LBRACE = 0x7b;
302 static const int RBRACE = 0x7d;
303
304 final String source;
305 final JsonListener listener;
306 JsonParser(this.source, this.listener);
307
308 /** Parses [source], or throws if it fails. */
309 void parse() {
310 final List<int> states = <int>[];
311 int state = STATE_INITIAL;
312 int position = 0;
313 int length = source.length;
314 while (position < length) {
315 int char = source.codeUnitAt(position);
316 switch (char) {
317 case SPACE:
318 case CARRIAGE_RETURN:
319 case NEWLINE:
320 case TAB:
321 position++;
322 break;
323 case QUOTE:
324 if ((state & ALLOW_STRING_MASK) != 0) fail(position);
325 position = parseString(position + 1);
326 state |= VALUE_READ_BITS;
327 break;
328 case LBRACKET:
329 if ((state & ALLOW_VALUE_MASK) != 0) fail(position);
330 listener.beginArray();
331 states.add(state);
332 state = STATE_ARRAY_EMPTY;
333 position++;
334 break;
335 case LBRACE:
336 if ((state & ALLOW_VALUE_MASK) != 0) fail(position);
337 listener.beginObject();
338 states.add(state);
339 state = STATE_OBJECT_EMPTY;
340 position++;
341 break;
342 case CHAR_n:
343 if ((state & ALLOW_VALUE_MASK) != 0) fail(position);
344 position = parseNull(position);
345 state |= VALUE_READ_BITS;
346 break;
347 case CHAR_f:
348 if ((state & ALLOW_VALUE_MASK) != 0) fail(position);
349 position = parseFalse(position);
350 state |= VALUE_READ_BITS;
351 break;
352 case CHAR_t:
353 if ((state & ALLOW_VALUE_MASK) != 0) fail(position);
354 position = parseTrue(position);
355 state |= VALUE_READ_BITS;
356 break;
357 case COLON:
358 if (state != STATE_OBJECT_KEY) fail(position);
359 listener.propertyName();
360 state = STATE_OBJECT_COLON;
361 position++;
362 break;
363 case COMMA:
364 if (state == STATE_OBJECT_VALUE) {
365 listener.propertyValue();
366 state = STATE_OBJECT_COMMA;
367 position++;
368 } else if (state == STATE_ARRAY_VALUE) {
369 listener.arrayElement();
370 state = STATE_ARRAY_COMMA;
371 position++;
372 } else {
373 fail(position);
374 }
375 break;
376 case RBRACKET:
377 if (state == STATE_ARRAY_EMPTY) {
378 listener.endArray();
379 } else if (state == STATE_ARRAY_VALUE) {
380 listener.arrayElement();
381 listener.endArray();
382 } else {
383 fail(position);
384 }
385 state = states.removeLast() | VALUE_READ_BITS;
386 position++;
387 break;
388 case RBRACE:
389 if (state == STATE_OBJECT_EMPTY) {
390 listener.endObject();
391 } else if (state == STATE_OBJECT_VALUE) {
392 listener.propertyValue();
393 listener.endObject();
394 } else {
395 fail(position);
396 }
397 state = states.removeLast() | VALUE_READ_BITS;
398 position++;
399 break;
400 default:
401 if ((state & ALLOW_VALUE_MASK) != 0) fail(position);
402 position = parseNumber(char, position);
403 state |= VALUE_READ_BITS;
404 break;
405 }
406 }
407 if (state != STATE_END) fail(position);
408 }
409
410 /**
411 * Parses a "true" literal starting at [position].
412 *
413 * [:source[position]:] must be "t".
414 */
415 int parseTrue(int position) {
416 assert(source.codeUnitAt(position) == CHAR_t);
417 if (source.length < position + 4) fail(position, "Unexpected identifier");
418 if (source.codeUnitAt(position + 1) != CHAR_r ||
419 source.codeUnitAt(position + 2) != CHAR_u ||
420 source.codeUnitAt(position + 3) != CHAR_e) {
421 fail(position);
422 }
423 listener.handleBool(true);
424 return position + 4;
425 }
426
427 /**
428 * Parses a "false" literal starting at [position].
429 *
430 * [:source[position]:] must be "f".
431 */
432 int parseFalse(int position) {
433 assert(source.codeUnitAt(position) == CHAR_f);
434 if (source.length < position + 5) fail(position, "Unexpected identifier");
435 if (source.codeUnitAt(position + 1) != CHAR_a ||
436 source.codeUnitAt(position + 2) != CHAR_l ||
437 source.codeUnitAt(position + 3) != CHAR_s ||
438 source.codeUnitAt(position + 4) != CHAR_e) {
439 fail(position);
440 }
441 listener.handleBool(false);
442 return position + 5;
443 }
444
445 /** Parses a "null" literal starting at [position].
446 *
447 * [:source[position]:] must be "n".
448 */
449 int parseNull(int position) {
450 assert(source.codeUnitAt(position) == CHAR_n);
451 if (source.length < position + 4) fail(position, "Unexpected identifier");
452 if (source.codeUnitAt(position + 1) != CHAR_u ||
453 source.codeUnitAt(position + 2) != CHAR_l ||
454 source.codeUnitAt(position + 3) != CHAR_l) {
455 fail(position);
456 }
457 listener.handleNull();
458 return position + 4;
459 }
460
461 int parseString(int position) {
462 // Format: '"'([^\x00-\x1f\\\"]|'\\'[bfnrt/\\"])*'"'
463 // Initial position is right after first '"'.
464 int start = position;
465 int char;
466 do {
467 if (position == source.length) {
468 fail(start - 1, "Unterminated string");
469 }
470 char = source.codeUnitAt(position);
471 if (char == QUOTE) {
472 listener.handleString(source.substring(start, position));
473 return position + 1;
474 }
475 if (char < SPACE) {
476 fail(position, "Control character in string");
477 }
478 position++;
479 } while (char != BACKSLASH);
480 // Backslash escape detected. Collect character codes for rest of string.
481 int firstEscape = position - 1;
482 List<int> chars = <int>[];
483 while (true) {
484 if (position == source.length) {
485 fail(start - 1, "Unterminated string");
486 }
487 char = source.codeUnitAt(position);
488 switch (char) {
489 case CHAR_b: char = BACKSPACE; break;
490 case CHAR_f: char = FORM_FEED; break;
491 case CHAR_n: char = NEWLINE; break;
492 case CHAR_r: char = CARRIAGE_RETURN; break;
493 case CHAR_t: char = TAB; break;
494 case SLASH:
495 case BACKSLASH:
496 case QUOTE:
497 break;
498 case CHAR_u:
499 int hexStart = position - 1;
500 int value = 0;
501 for (int i = 0; i < 4; i++) {
502 position++;
503 if (position == source.length) {
504 fail(start - 1, "Unterminated string");
505 }
506 char = source.codeUnitAt(position);
507 char -= 0x30;
508 if (char < 0) fail(hexStart, "Invalid unicode escape");
509 if (char < 10) {
510 value = value * 16 + char;
511 } else {
512 char = (char | 0x20) - 0x31;
513 if (char < 0 || char > 5) {
514 fail(hexStart, "Invalid unicode escape");
515 }
516 value = value * 16 + char + 10;
517 }
518 }
519 char = value;
520 break;
521 default:
522 if (char < SPACE) fail(position, "Control character in string");
523 fail(position, "Unrecognized string escape");
524 }
525 do {
526 chars.add(char);
527 position++;
528 if (position == source.length) fail(start - 1, "Unterminated string");
529 char = source.codeUnitAt(position);
530 if (char == QUOTE) {
531 String result = new String.fromCharCodes(chars);
532 if (start < firstEscape) {
533 result = "${source.substring(start, firstEscape)}$result";
534 }
535 listener.handleString(result);
536 return position + 1;
537 }
538 if (char < SPACE) {
539 fail(position, "Control character in string");
540 }
541 } while (char != BACKSLASH);
542 position++;
543 }
544 }
545
546 int _handleLiteral(start, position, isDouble) {
547 String literal = source.substring(start, position);
548 // This correctly creates -0 for doubles.
549 num value = (isDouble ? double.parse(literal) : int.parse(literal));
550 listener.handleNumber(value);
551 return position;
552 }
553
554 int parseNumber(int char, int position) {
555 // Format:
556 // '-'?('0'|[1-9][0-9]*)('.'[0-9]+)?([eE][+-]?[0-9]+)?
557 int start = position;
558 int length = source.length;
559 bool isDouble = false;
560 if (char == MINUS) {
561 position++;
562 if (position == length) fail(position, "Missing expected digit");
563 char = source.codeUnitAt(position);
564 }
565 if (char < CHAR_0 || char > CHAR_9) {
566 fail(position, "Missing expected digit");
567 }
568 if (char == CHAR_0) {
569 position++;
570 if (position == length) return _handleLiteral(start, position, false);
571 char = source.codeUnitAt(position);
572 if (CHAR_0 <= char && char <= CHAR_9) {
573 fail(position);
574 }
575 } else {
576 do {
577 position++;
578 if (position == length) return _handleLiteral(start, position, false);
579 char = source.codeUnitAt(position);
580 } while (CHAR_0 <= char && char <= CHAR_9);
581 }
582 if (char == DECIMALPOINT) {
583 isDouble = true;
584 position++;
585 if (position == length) fail(position, "Missing expected digit");
586 char = source.codeUnitAt(position);
587 if (char < CHAR_0 || char > CHAR_9) fail(position);
588 do {
589 position++;
590 if (position == length) return _handleLiteral(start, position, true);
591 char = source.codeUnitAt(position);
592 } while (CHAR_0 <= char && char <= CHAR_9);
593 }
594 if (char == CHAR_e || char == CHAR_E) {
595 isDouble = true;
596 position++;
597 if (position == length) fail(position, "Missing expected digit");
598 char = source.codeUnitAt(position);
599 if (char == PLUS || char == MINUS) {
600 position++;
601 if (position == length) fail(position, "Missing expected digit");
602 char = source.codeUnitAt(position);
603 }
604 if (char < CHAR_0 || char > CHAR_9) {
605 fail(position, "Missing expected digit");
606 }
607 do {
608 position++;
609 if (position == length) return _handleLiteral(start, position, true);
610 char = source.codeUnitAt(position);
611 } while (CHAR_0 <= char && char <= CHAR_9);
612 }
613 return _handleLiteral(start, position, isDouble);
614 }
615
616 void fail(int position, [String message]) {
617 if (message == null) message = "Unexpected character";
618 listener.fail(source, position, message);
619 // If the listener didn't throw, do it here.
620 String slice;
621 int sliceEnd = position + 20;
622 if (sliceEnd > source.length) {
623 slice = "'${source.substring(position)}'";
624 } else {
625 slice = "'${source.substring(position, sliceEnd)}...'";
626 }
627 throw new FormatException("Unexpected character at $position: $slice");
628 }
629 }
OLDNEW
« no previous file with comments | « pkg/json/README.md ('k') | pkg/json/pubspec.yaml » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698