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

Side by Side Diff: tool/input_sdk/lib/convert/json.dart

Issue 1965563003: Update dart:convert and dart:core Uri. (Closed) Base URL: https://github.com/dart-lang/dev_compiler.git@master
Patch Set: Created 4 years, 7 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
OLDNEW
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 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. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 part of dart.convert; 5 part of dart.convert;
6 6
7 /** 7 /**
8 * Error thrown by JSON serialization if an object cannot be serialized. 8 * Error thrown by JSON serialization if an object cannot be serialized.
9 * 9 *
10 * The [unsupportedObject] field holds that object that failed to be serialized. 10 * The [unsupportedObject] field holds that object that failed to be serialized.
11 * 11 *
12 * If an object isn't directly serializable, the serializer calls the 'toJson' 12 * If an object isn't directly serializable, the serializer calls the `toJson`
13 * method on the object. If that call fails, the error will be stored in the 13 * method on the object. If that call fails, the error will be stored in the
14 * [cause] field. If the call returns an object that isn't directly 14 * [cause] field. If the call returns an object that isn't directly
15 * serializable, the [cause] is be null. 15 * serializable, the [cause] is null.
16 */ 16 */
17 class JsonUnsupportedObjectError extends Error { 17 class JsonUnsupportedObjectError extends Error {
18 /** The object that could not be serialized. */ 18 /** The object that could not be serialized. */
19 final unsupportedObject; 19 final unsupportedObject;
20 /** The exception thrown when trying to convert the object. */ 20 /** The exception thrown when trying to convert the object. */
21 final cause; 21 final cause;
22 22
23 JsonUnsupportedObjectError(this.unsupportedObject, { this.cause }); 23 JsonUnsupportedObjectError(this.unsupportedObject, { this.cause });
24 24
25 String toString() { 25 String toString() {
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
58 */ 58 */
59 const JsonCodec JSON = const JsonCodec(); 59 const JsonCodec JSON = const JsonCodec();
60 60
61 typedef _Reviver(var key, var value); 61 typedef _Reviver(var key, var value);
62 typedef _ToEncodable(var o); 62 typedef _ToEncodable(var o);
63 63
64 64
65 /** 65 /**
66 * A [JsonCodec] encodes JSON objects to strings and decodes strings to 66 * A [JsonCodec] encodes JSON objects to strings and decodes strings to
67 * JSON objects. 67 * JSON objects.
68 *
69 * Examples:
70 *
71 * var encoded = JSON.encode([1, 2, { "a": null }]);
72 * var decoded = JSON.decode('["foo", { "bar": 499 }]');
68 */ 73 */
69 class JsonCodec extends Codec<Object, String> { 74 class JsonCodec extends Codec<Object, String> {
70 final _Reviver _reviver; 75 final _Reviver _reviver;
71 final _ToEncodable _toEncodable; 76 final _ToEncodable _toEncodable;
72 77
73 /** 78 /**
74 * Creates a `JsonCodec` with the given reviver and encoding function. 79 * Creates a `JsonCodec` with the given reviver and encoding function.
75 * 80 *
76 * The [reviver] function is called during decoding. It is invoked 81 * The [reviver] function is called during decoding. It is invoked once for
77 * once for each object or list property that has been parsed. 82 * each object or list property that has been parsed.
78 * The `key` argument is either the 83 * The `key` argument is either the integer list index for a list property,
79 * integer list index for a list property, the string map key for object 84 * the string map key for object properties, or `null` for the final result.
80 * properties, or `null` for the final result.
81 * 85 *
82 * If [reviver] is omitted, it defaults to returning the value argument. 86 * If [reviver] is omitted, it defaults to returning the value argument.
83 * 87 *
84 * The [toEncodable] function is used during encoding. It is invoked for 88 * The [toEncodable] function is used during encoding. It is invoked for
85 * values that are not directly encodable to a JSON1toE 89 * values that are not directly encodable to a string (a value that is not a
86 * string (a value that is not a number, boolean, string, null, list or a map 90 * number, boolean, string, null, list or a map with string keys). The
87 * with string keys). The function must return an object that is directly 91 * function must return an object that is directly encodable. The elements of
88 * encodable. The elements of a returned list and values of a returned map 92 * a returned list and values of a returned map do not need to be directly
89 * do not need be directly encodable, and if they aren't, `toEncodable` will 93 * encodable, and if they aren't, `toEncodable` will be used on them as well.
90 * be used on them as well. 94 * Please notice that it is possible to cause an infinite recursive regress
91 * Please notice that it is possible to cause an infinite recursive 95 * in this way, by effectively creating an infinite data structure through
92 * regress in this way, by effectively creating an infinite data structure 96 * repeated call to `toEncodable`.
93 * through repeated call to `toEncodable`.
94 * 97 *
95 * If [toEncodable] is omitted, it defaults to a function that returns the 98 * If [toEncodable] is omitted, it defaults to a function that returns the
96 * result of calling `.toJson()` on the unencodable object. 99 * result of calling `.toJson()` on the unencodable object.
97 */ 100 */
98 const JsonCodec({reviver(var key, var value), toEncodable(var object)}) 101 const JsonCodec({reviver(var key, var value), toEncodable(var object)})
99 : _reviver = reviver, 102 : _reviver = reviver,
100 _toEncodable = toEncodable; 103 _toEncodable = toEncodable;
101 104
102 /** 105 /**
103 * Creates a `JsonCodec` with the given reviver. 106 * Creates a `JsonCodec` with the given reviver.
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
149 152
150 JsonDecoder get decoder { 153 JsonDecoder get decoder {
151 if (_reviver == null) return const JsonDecoder(); 154 if (_reviver == null) return const JsonDecoder();
152 return new JsonDecoder(_reviver); 155 return new JsonDecoder(_reviver);
153 } 156 }
154 } 157 }
155 158
156 /** 159 /**
157 * This class converts JSON objects to strings. 160 * This class converts JSON objects to strings.
158 */ 161 */
159 class JsonEncoder extends Converter<Object, String> { 162 class JsonEncoder extends ChunkedConverter<Object, String, Object, String> {
160 /** 163 /**
161 * The string used for indention. 164 * The string used for indention.
162 * 165 *
163 * When generating multi-line output, this string is inserted once at the 166 * When generating multi-line output, this string is inserted once at the
164 * beginning of each indented line for each level of indentation. 167 * beginning of each indented line for each level of indentation.
165 * 168 *
166 * If `null`, the output is encoded as a single line. 169 * If `null`, the output is encoded as a single line.
167 */ 170 */
168 final String indent; 171 final String indent;
169 172
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
208 * the object. 211 * the object.
209 */ 212 */
210 const JsonEncoder.withIndent(this.indent, 213 const JsonEncoder.withIndent(this.indent,
211 [Object toEncodable(Object nonSerializable)]) 214 [Object toEncodable(Object nonSerializable)])
212 : this._toEncodable = toEncodable; 215 : this._toEncodable = toEncodable;
213 216
214 /** 217 /**
215 * Converts [object] to a JSON [String]. 218 * Converts [object] to a JSON [String].
216 * 219 *
217 * Directly serializable values are [num], [String], [bool], and [Null], as 220 * Directly serializable values are [num], [String], [bool], and [Null], as
218 * well as some [List] and [Map] values. 221 * well as some [List] and [Map] values. For [List], the elements must all be
219 * For [List], the elements must all be serializable. 222 * serializable. For [Map], the keys must be [String] and the values must be
220 * For [Map], the keys must be [String] and the values must be serializable. 223 * serializable.
221 * 224 *
222 * If a value is any other type is attempted serialized, the conversion 225 * If a value of any other type is attempted to be serialized, the
223 * function provided in the constructor is invoked with the object as argument 226 * `toEncodable` function provided in the constructor is called with the value
224 * and the result, which must be a directly serializable value, 227 * as argument. The result, which must be a directly serializable value, is
225 * is serialized instead of the original value. 228 * serialized instead of the original value.
226 * 229 *
227 * If the conversion throws, or returns a value that is not directly 230 * If the conversion throws, or returns a value that is not directly
228 * serializable, a [JsonUnsupportedObjectError] exception is thrown. 231 * serializable, a [JsonUnsupportedObjectError] exception is thrown.
229 * If the call throws, the error is caught and stored in the 232 * If the call throws, the error is caught and stored in the
230 * [JsonUnsupportedObjectError]'s [:cause:] field. 233 * [JsonUnsupportedObjectError]'s [:cause:] field.
231 * 234 *
232 * If a [List] or [Map] contains a reference to itself, directly or through 235 * If a [List] or [Map] contains a reference to itself, directly or through
233 * other lists or maps, it cannot be serialized and a [JsonCyclicError] is 236 * other lists or maps, it cannot be serialized and a [JsonCyclicError] is
234 * thrown. 237 * thrown.
235 * 238 *
(...skipping 19 matching lines...) Expand all
255 if (sink is! StringConversionSink) { 258 if (sink is! StringConversionSink) {
256 sink = new StringConversionSink.from(sink); 259 sink = new StringConversionSink.from(sink);
257 } else if (sink is _Utf8EncoderSink) { 260 } else if (sink is _Utf8EncoderSink) {
258 return new _JsonUtf8EncoderSink(sink._sink, _toEncodable, 261 return new _JsonUtf8EncoderSink(sink._sink, _toEncodable,
259 JsonUtf8Encoder._utf8Encode(indent), 262 JsonUtf8Encoder._utf8Encode(indent),
260 JsonUtf8Encoder.DEFAULT_BUFFER_SIZE); 263 JsonUtf8Encoder.DEFAULT_BUFFER_SIZE);
261 } 264 }
262 return new _JsonEncoderSink(sink, _toEncodable, indent); 265 return new _JsonEncoderSink(sink, _toEncodable, indent);
263 } 266 }
264 267
265 // Override the base-classes bind, to provide a better type. 268 // Override the base class's bind, to provide a better type.
266 Stream<String> bind(Stream<Object> stream) => super.bind(stream); 269 Stream<String> bind(Stream<Object> stream) => super.bind(stream);
267 270
268 Converter<Object, dynamic> fuse(Converter<String, dynamic> other) { 271 Converter<Object, dynamic> fuse(Converter<String, dynamic> other) {
269 if (other is Utf8Encoder) { 272 if (other is Utf8Encoder) {
270 return new JsonUtf8Encoder(indent, _toEncodable); 273 return new JsonUtf8Encoder(indent, _toEncodable);
271 } 274 }
272 return super.fuse(other); 275 return super.fuse(other);
273 } 276 }
274 } 277 }
275 278
276 /** 279 /**
277 * Encoder that encodes a single object as a UTF-8 encoded JSON string. 280 * Encoder that encodes a single object as a UTF-8 encoded JSON string.
278 * 281 *
279 * This encoder works equivalently to first converting the object to 282 * This encoder works equivalently to first converting the object to
280 * a JSON string, and then UTF-8 encoding the string, but without 283 * a JSON string, and then UTF-8 encoding the string, but without
281 * creating an intermediate string. 284 * creating an intermediate string.
282 */ 285 */
283 class JsonUtf8Encoder extends Converter<Object, List<int>> { 286 class JsonUtf8Encoder extends
287 ChunkedConverter<Object, List<int>, Object, List<int>> {
284 /** Default buffer size used by the JSON-to-UTF-8 encoder. */ 288 /** Default buffer size used by the JSON-to-UTF-8 encoder. */
285 static const int DEFAULT_BUFFER_SIZE = 256; 289 static const int DEFAULT_BUFFER_SIZE = 256;
286 /** Indentation used in pretty-print mode, `null` if not pretty. */ 290 /** Indentation used in pretty-print mode, `null` if not pretty. */
287 final List<int> _indent; 291 final List<int> _indent;
288 /** Function called with each un-encodable object encountered. */ 292 /** Function called with each un-encodable object encountered. */
289 final Function _toEncodable; 293 final Function _toEncodable;
290 /** UTF-8 buffer size. */ 294 /** UTF-8 buffer size. */
291 final int _bufferSize; 295 final int _bufferSize;
292 296
293 /** 297 /**
294 * Create converter. 298 * Create converter.
295 * 299 *
296 * If [indent] is non-`null`, the converter attempts to "pretty-print" the 300 * If [indent] is non-`null`, the converter attempts to "pretty-print" the
297 * JSON, and uses `indent` as the indentation. Otherwise the result has no 301 * JSON, and uses `indent` as the indentation. Otherwise the result has no
298 * whitespace outside of string literals. 302 * whitespace outside of string literals.
299 * If `indent` contains characters that are not valid JSON whitespace 303 * If `indent` contains characters that are not valid JSON whitespace
300 * characters, the result will not be valid JSON. JSON whitespace characters 304 * characters, the result will not be valid JSON. JSON whitespace characters
301 * are space (U+0020), tab (U+0009), line feed (U+000a) and carriage return 305 * are space (U+0020), tab (U+0009), line feed (U+000a) and carriage return
302 * (U+000d) (ECMA 404). 306 * (U+000d) ([ECMA
307 * 404](http://www.ecma-international.org/publications/standards/Ecma-404.htm) ).
303 * 308 *
304 * The [bufferSize] is the size of the internal buffers used to collect 309 * The [bufferSize] is the size of the internal buffers used to collect
305 * UTF-8 code units. 310 * UTF-8 code units.
306 * If using [startChunkedConversion], it will be the size of the chunks. 311 * If using [startChunkedConversion], it will be the size of the chunks.
307 * 312 *
308 * The JSON encoder handles numbers, strings, booleans, null, lists and 313 * The JSON encoder handles numbers, strings, booleans, null, lists and maps
309 * maps directly. 314 * directly.
310 * 315 *
311 * Any other object is attempted converted by [toEncodable] to an 316 * Any other object is attempted converted by [toEncodable] to an object that
312 * object that is of one of the convertible types. 317 * is of one of the convertible types.
313 * 318 *
314 * If [toEncodable] is omitted, it defaults to calling `.toJson()` on 319 * If [toEncodable] is omitted, it defaults to calling `.toJson()` on the
315 * the object. 320 * object.
316 */ 321 */
317 JsonUtf8Encoder([String indent, 322 JsonUtf8Encoder([String indent,
318 toEncodable(Object object), 323 toEncodable(Object object),
319 int bufferSize = DEFAULT_BUFFER_SIZE]) 324 int bufferSize = DEFAULT_BUFFER_SIZE])
320 : _indent = _utf8Encode(indent), 325 : _indent = _utf8Encode(indent),
321 _toEncodable = toEncodable, 326 _toEncodable = toEncodable,
322 _bufferSize = bufferSize; 327 _bufferSize = bufferSize;
323 328
324 static List<int> _utf8Encode(String string) { 329 static List<int> _utf8Encode(String string) {
325 if (string == null) return null; 330 if (string == null) return null;
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
379 ByteConversionSink byteSink; 384 ByteConversionSink byteSink;
380 if (sink is ByteConversionSink) { 385 if (sink is ByteConversionSink) {
381 byteSink = sink; 386 byteSink = sink;
382 } else { 387 } else {
383 byteSink = new ByteConversionSink.from(sink); 388 byteSink = new ByteConversionSink.from(sink);
384 } 389 }
385 return new _JsonUtf8EncoderSink(byteSink, _toEncodable, 390 return new _JsonUtf8EncoderSink(byteSink, _toEncodable,
386 _indent, _bufferSize); 391 _indent, _bufferSize);
387 } 392 }
388 393
389 // Override the base-classes bind, to provide a better type. 394 // Override the base class's bind, to provide a better type.
390 Stream<List<int>> bind(Stream<Object> stream) { 395 Stream<List<int>> bind(Stream<Object> stream) {
391 return super.bind(stream); 396 return super.bind(stream);
392 } 397 }
393 398
394 Converter<Object, dynamic> fuse(Converter<List<int>, dynamic> other) { 399 Converter<Object, dynamic> fuse(Converter<List<int>, dynamic> other) {
395 return super.fuse(other); 400 return super.fuse(other);
396 } 401 }
397 } 402 }
398 403
399 /** 404 /**
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after
462 if (!_isDone) { 467 if (!_isDone) {
463 _isDone = true; 468 _isDone = true;
464 _sink.close(); 469 _sink.close();
465 } 470 }
466 } 471 }
467 } 472 }
468 473
469 /** 474 /**
470 * This class parses JSON strings and builds the corresponding objects. 475 * This class parses JSON strings and builds the corresponding objects.
471 */ 476 */
472 class JsonDecoder extends Converter<String, Object> { 477 class JsonDecoder extends ChunkedConverter<String, Object, String, Object> {
473 final _Reviver _reviver; 478 final _Reviver _reviver;
474 /** 479 /**
475 * Constructs a new JsonDecoder. 480 * Constructs a new JsonDecoder.
476 * 481 *
477 * The [reviver] may be `null`. 482 * The [reviver] may be `null`.
478 */ 483 */
479 const JsonDecoder([reviver(var key, var value)]) : this._reviver = reviver; 484 const JsonDecoder([reviver(var key, var value)]) : this._reviver = reviver;
480 485
481 /** 486 /**
482 * Converts the given JSON-string [input] to its corresponding object. 487 * Converts the given JSON-string [input] to its corresponding object.
483 * 488 *
484 * Parsed JSON values are of the types [num], [String], [bool], [Null], 489 * Parsed JSON values are of the types [num], [String], [bool], [Null],
485 * [List]s of parsed JSON values or [Map]s from [String] to parsed 490 * [List]s of parsed JSON values or [Map]s from [String] to parsed JSON
486 * JSON values. 491 * values.
487 * 492 *
488 * If `this` was initialized with a reviver, then the parsing operation 493 * If `this` was initialized with a reviver, then the parsing operation
489 * invokes the reviver on every object or list property that has been parsed. 494 * invokes the reviver on every object or list property that has been parsed.
490 * The arguments are the property name ([String]) or list index ([int]), and 495 * The arguments are the property name ([String]) or list index ([int]), and
491 * the value is the parsed value. The return value of the reviver is used as 496 * the value is the parsed value. The return value of the reviver is used as
492 * the value of that property instead the parsed value. 497 * the value of that property instead the parsed value.
493 * 498 *
494 * Throws [FormatException] if the input is not valid JSON text. 499 * Throws [FormatException] if the input is not valid JSON text.
495 */ 500 */
496 dynamic convert(String input) => _parseJson(input, _reviver); 501 dynamic convert(String input) => _parseJson(input, _reviver);
497 502
498 /** 503 /**
499 * Starts a conversion from a chunked JSON string to its corresponding 504 * Starts a conversion from a chunked JSON string to its corresponding object.
500 * object.
501 * 505 *
502 * The output [sink] receives exactly one decoded element through `add`. 506 * The output [sink] receives exactly one decoded element through `add`.
503 */ 507 */
504 external StringConversionSink startChunkedConversion(Sink<Object> sink); 508 external StringConversionSink startChunkedConversion(Sink<Object> sink);
505 509
506 // Override the base-classes bind, to provide a better type. 510 // Override the base class's bind, to provide a better type.
507 Stream<Object> bind(Stream<String> stream) => super.bind(stream); 511 Stream<Object> bind(Stream<String> stream) => super.bind(stream);
508 } 512 }
509 513
510 // Internal optimized JSON parsing implementation. 514 // Internal optimized JSON parsing implementation.
511 external _parseJson(String source, reviver(key, value)); 515 external _parseJson(String source, reviver(key, value));
512 516
513 517
514 // Implementation of encoder/stringifier. 518 // Implementation of encoder/stringifier.
515 519
516 Object _defaultToEncodable(object) => object.toJson(); 520 Object _defaultToEncodable(object) => object.toJson();
(...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after
606 if (offset == 0) { 610 if (offset == 0) {
607 writeString(s); 611 writeString(s);
608 } else if (offset < length) { 612 } else if (offset < length) {
609 writeStringSlice(s, offset, length); 613 writeStringSlice(s, offset, length);
610 } 614 }
611 } 615 }
612 616
613 /** 617 /**
614 * Check if an encountered object is already being traversed. 618 * Check if an encountered object is already being traversed.
615 * 619 *
616 * Records the object if it isn't already seen. 620 * Records the object if it isn't already seen. Should have a matching call to
617 * Should have a matching call to [_removeSeen] when the object 621 * [_removeSeen] when the object is no longer being traversed.
618 * is no longer being traversed.
619 */ 622 */
620 void _checkCycle(object) { 623 void _checkCycle(object) {
621 for (int i = 0; i < _seen.length; i++) { 624 for (int i = 0; i < _seen.length; i++) {
622 if (identical(object, _seen[i])) { 625 if (identical(object, _seen[i])) {
623 throw new JsonCyclicError(object); 626 throw new JsonCyclicError(object);
624 } 627 }
625 } 628 }
626 _seen.add(object); 629 _seen.add(object);
627 } 630 }
628 631
629 /** 632 /**
630 * Removes object from the list of currently traversed objects. 633 * Remove [object] from the list of currently traversed objects.
631 * 634 *
632 * Should be called in the opposite order of the matching [_checkCycle] 635 * Should be called in the opposite order of the matching [_checkCycle]
633 * calls. 636 * calls.
634 */ 637 */
635 void _removeSeen(object) { 638 void _removeSeen(object) {
636 assert(!_seen.isEmpty); 639 assert(!_seen.isEmpty);
637 assert(identical(_seen.last, object)); 640 assert(identical(_seen.last, object));
638 _seen.removeLast(); 641 _seen.removeLast();
639 } 642 }
640 643
641 /** 644 /**
642 * Writes an object. 645 * Write an object.
643 * 646 *
644 * If the object isn't directly encodable, the [_toEncodable] function 647 * If [object] isn't directly encodable, the [_toEncodable] function gets one
645 * gets one chance to return a replacement which is encodable. 648 * chance to return a replacement which is encodable.
646 */ 649 */
647 void writeObject(object) { 650 void writeObject(object) {
648 // Tries stringifying object directly. If it's not a simple value, List or 651 // Tries stringifying object directly. If it's not a simple value, List or
649 // Map, call toJson() to get a custom representation and try serializing 652 // Map, call toJson() to get a custom representation and try serializing
650 // that. 653 // that.
651 if (writeJsonValue(object)) return; 654 if (writeJsonValue(object)) return;
652 _checkCycle(object); 655 _checkCycle(object);
653 try { 656 try {
654 var customJson = _toEncodable(object); 657 var customJson = _toEncodable(object);
655 if (!writeJsonValue(customJson)) { 658 if (!writeJsonValue(customJson)) {
656 throw new JsonUnsupportedObjectError(object); 659 throw new JsonUnsupportedObjectError(object);
657 } 660 }
658 _removeSeen(object); 661 _removeSeen(object);
659 } catch (e) { 662 } catch (e) {
660 throw new JsonUnsupportedObjectError(object, cause: e); 663 throw new JsonUnsupportedObjectError(object, cause: e);
661 } 664 }
662 } 665 }
663 666
664 /** 667 /**
665 * Serializes a [num], [String], [bool], [Null], [List] or [Map] value. 668 * Serialize a [num], [String], [bool], [Null], [List] or [Map] value.
666 * 669 *
667 * Returns true if the value is one of these types, and false if not. 670 * Returns true if the value is one of these types, and false if not.
668 * If a value is both a [List] and a [Map], it's serialized as a [List]. 671 * If a value is both a [List] and a [Map], it's serialized as a [List].
669 */ 672 */
670 bool writeJsonValue(object) { 673 bool writeJsonValue(object) {
671 if (object is num) { 674 if (object is num) {
672 if (!object.isFinite) return false; 675 if (!object.isFinite) return false;
673 writeNumber(object); 676 writeNumber(object);
674 return true; 677 return true;
675 } else if (identical(object, true)) { 678 } else if (identical(object, true)) {
(...skipping 10 matching lines...) Expand all
686 writeStringContent(object); 689 writeStringContent(object);
687 writeString('"'); 690 writeString('"');
688 return true; 691 return true;
689 } else if (object is List) { 692 } else if (object is List) {
690 _checkCycle(object); 693 _checkCycle(object);
691 writeList(object); 694 writeList(object);
692 _removeSeen(object); 695 _removeSeen(object);
693 return true; 696 return true;
694 } else if (object is Map) { 697 } else if (object is Map) {
695 _checkCycle(object); 698 _checkCycle(object);
696 writeMap(object); 699 // writeMap can fail if keys are not all strings.
700 var success = writeMap(object);
697 _removeSeen(object); 701 _removeSeen(object);
698 return true; 702 return success;
699 } else { 703 } else {
700 return false; 704 return false;
701 } 705 }
702 } 706 }
703 707
704 /** Serializes a [List]. */ 708 /** Serialize a [List]. */
705 void writeList(List list) { 709 void writeList(List list) {
706 writeString('['); 710 writeString('[');
707 if (list.length > 0) { 711 if (list.length > 0) {
708 writeObject(list[0]); 712 writeObject(list[0]);
709 for (int i = 1; i < list.length; i++) { 713 for (int i = 1; i < list.length; i++) {
710 writeString(','); 714 writeString(',');
711 writeObject(list[i]); 715 writeObject(list[i]);
712 } 716 }
713 } 717 }
714 writeString(']'); 718 writeString(']');
715 } 719 }
716 720
717 /** Serializes a [Map]. */ 721 /** Serialize a [Map]. */
718 void writeMap(Map<String, Object> map) { 722 bool writeMap(Map<String, Object> map) {
723 if (map.isEmpty) {
724 writeString("{}");
725 return true;
726 }
727 List keyValueList = new List(map.length * 2);
728 int i = 0;
729 bool allStringKeys = true;
730 map.forEach((key, value) {
731 if (key is! String) {
732 allStringKeys = false;
733 }
734 keyValueList[i++] = key;
735 keyValueList[i++] = value;
736 });
737 if (!allStringKeys) return false;
719 writeString('{'); 738 writeString('{');
720 String separator = '"'; 739 String separator = '"';
721 map.forEach((String key, value) { 740 for (int i = 0; i < keyValueList.length; i += 2) {
722 writeString(separator); 741 writeString(separator);
723 separator = ',"'; 742 separator = ',"';
724 writeStringContent(key); 743 writeStringContent(keyValueList[i]);
725 writeString('":'); 744 writeString('":');
726 writeObject(value); 745 writeObject(keyValueList[i + 1]);
727 }); 746 }
728 writeString('}'); 747 writeString('}');
748 return true;
729 } 749 }
730 } 750 }
731 751
732 /** 752 /**
733 * A modification of [_JsonStringifier] which indents the contents of [List] and 753 * A modification of [_JsonStringifier] which indents the contents of [List] and
734 * [Map] objects using the specified indent value. 754 * [Map] objects using the specified indent value.
735 * 755 *
736 * Subclasses should implement [writeIndentation]. 756 * Subclasses should implement [writeIndentation].
737 */ 757 */
738 abstract class _JsonPrettyPrintMixin implements _JsonStringifier { 758 abstract class _JsonPrettyPrintMixin implements _JsonStringifier {
(...skipping 17 matching lines...) Expand all
756 writeIndentation(_indentLevel); 776 writeIndentation(_indentLevel);
757 writeObject(list[i]); 777 writeObject(list[i]);
758 } 778 }
759 writeString('\n'); 779 writeString('\n');
760 _indentLevel--; 780 _indentLevel--;
761 writeIndentation(_indentLevel); 781 writeIndentation(_indentLevel);
762 writeString(']'); 782 writeString(']');
763 } 783 }
764 } 784 }
765 785
766 void writeMap(Map map) { 786 bool writeMap(Map map) {
767 if (map.isEmpty) { 787 if (map.isEmpty) {
768 writeString('{}'); 788 writeString("{}");
769 } else { 789 return true;
770 writeString('{\n'); 790 }
771 _indentLevel++; 791 List keyValueList = new List(map.length * 2);
772 bool first = true; 792 int i = 0;
773 map.forEach((String key, Object value) { 793 bool allStringKeys = true;
774 if (!first) { 794 map.forEach((key, value) {
775 writeString(",\n"); 795 if (key is! String) {
776 } 796 allStringKeys = false;
777 writeIndentation(_indentLevel); 797 }
778 writeString('"'); 798 keyValueList[i++] = key;
779 writeStringContent(key); 799 keyValueList[i++] = value;
780 writeString('": '); 800 });
781 writeObject(value); 801 if (!allStringKeys) return false;
782 first = false; 802 writeString('{\n');
783 }); 803 _indentLevel++;
784 writeString('\n'); 804 String separator = "";
785 _indentLevel--; 805 for (int i = 0; i < keyValueList.length; i += 2) {
806 writeString(separator);
807 separator = ",\n";
786 writeIndentation(_indentLevel); 808 writeIndentation(_indentLevel);
787 writeString('}'); 809 writeString('"');
810 writeStringContent(keyValueList[i]);
811 writeString('": ');
812 writeObject(keyValueList[i + 1]);
788 } 813 }
814 writeString('\n');
815 _indentLevel--;
816 writeIndentation(_indentLevel);
817 writeString('}');
818 return true;
789 } 819 }
790 } 820 }
791 821
792 /** 822 /**
793 * A specialziation of [_JsonStringifier] that writes its JSON to a string. 823 * A specialziation of [_JsonStringifier] that writes its JSON to a string.
794 */ 824 */
795 class _JsonStringStringifier extends _JsonStringifier { 825 class _JsonStringStringifier extends _JsonStringifier {
796 final StringSink _sink; 826 final StringSink _sink;
797 827
798 _JsonStringStringifier(this._sink, _toEncodable) : super(_toEncodable); 828 _JsonStringStringifier(this._sink, _toEncodable) : super(_toEncodable);
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after
866 */ 896 */
867 class _JsonUtf8Stringifier extends _JsonStringifier { 897 class _JsonUtf8Stringifier extends _JsonStringifier {
868 final int bufferSize; 898 final int bufferSize;
869 final Function addChunk; 899 final Function addChunk;
870 Uint8List buffer; 900 Uint8List buffer;
871 int index = 0; 901 int index = 0;
872 902
873 _JsonUtf8Stringifier(toEncodable, int bufferSize, this.addChunk) 903 _JsonUtf8Stringifier(toEncodable, int bufferSize, this.addChunk)
874 : this.bufferSize = bufferSize, 904 : this.bufferSize = bufferSize,
875 buffer = new Uint8List(bufferSize), 905 buffer = new Uint8List(bufferSize),
876 super(toEncodable) 906 super(toEncodable);
877 ;
878 907
879 /** 908 /**
880 * Convert [object] to UTF-8 encoded JSON. 909 * Convert [object] to UTF-8 encoded JSON.
881 * 910 *
882 * Calls [addChunk] with slices of UTF-8 code units. 911 * Calls [addChunk] with slices of UTF-8 code units.
883 * These will typically have size [bufferSize], but may be shorter. 912 * These will typically have size [bufferSize], but may be shorter.
884 * The buffers are not reused, so the [addChunk] call may keep and reuse 913 * The buffers are not reused, so the [addChunk] call may keep and reuse the
885 * the chunks. 914 * chunks.
886 * 915 *
887 * If [indent] is non-`null`, the result will be "pretty-printed" with 916 * If [indent] is non-`null`, the result will be "pretty-printed" with extra
888 * extra newlines and indentation, using [indent] as the indentation. 917 * newlines and indentation, using [indent] as the indentation.
889 */ 918 */
890 static void stringify(Object object, 919 static void stringify(Object object,
891 List<int> indent, 920 List<int> indent,
892 toEncodableFunction(Object o), 921 toEncodableFunction(Object o),
893 int bufferSize, 922 int bufferSize,
894 void addChunk(Uint8List chunk, int start, int end)) { 923 void addChunk(Uint8List chunk, int start, int end)) {
895 _JsonUtf8Stringifier stringifier; 924 _JsonUtf8Stringifier stringifier;
896 if (indent != null) { 925 if (indent != null) {
897 stringifier = new _JsonUtf8StringifierPretty(toEncodableFunction, indent, 926 stringifier = new _JsonUtf8StringifierPretty(toEncodableFunction, indent,
898 bufferSize, addChunk); 927 bufferSize, addChunk);
(...skipping 131 matching lines...) Expand 10 before | Expand all | Expand 10 after
1030 buffer.setRange(index, end, indent); 1059 buffer.setRange(index, end, indent);
1031 index = end; 1060 index = end;
1032 } else { 1061 } else {
1033 for (int i = 0; i < indentLength; i++) { 1062 for (int i = 0; i < indentLength; i++) {
1034 writeByte(indent[i]); 1063 writeByte(indent[i]);
1035 } 1064 }
1036 } 1065 }
1037 } 1066 }
1038 } 1067 }
1039 } 1068 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698