OLD | NEW |
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. |
(...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
132 * Converts [value] to a JSON string. | 132 * Converts [value] to a JSON string. |
133 * | 133 * |
134 * If value contains objects that are not directly encodable to a JSON | 134 * If value contains objects that are not directly encodable to a JSON |
135 * string (a value that is not a number, boolean, string, null, list or a map | 135 * string (a value that is not a number, boolean, string, null, list or a map |
136 * with string keys), the [toEncodable] function is used to convert it to an | 136 * with string keys), the [toEncodable] function is used to convert it to an |
137 * object that must be directly encodable. | 137 * object that must be directly encodable. |
138 * | 138 * |
139 * If [toEncodable] is omitted, it defaults to a function that returns the | 139 * If [toEncodable] is omitted, it defaults to a function that returns the |
140 * result of calling `.toJson()` on the unencodable object. | 140 * result of calling `.toJson()` on the unencodable object. |
141 */ | 141 */ |
142 String encode(Object value, {toEncodable(var object)}) { | 142 String encode(Object value, {toEncodable(object)}) { |
143 if (toEncodable == null) toEncodable = _toEncodable; | 143 if (toEncodable == null) toEncodable = _toEncodable; |
144 if (toEncodable == null) return encoder.convert(value); | 144 if (toEncodable == null) return encoder.convert(value); |
145 return new JsonEncoder(toEncodable).convert(value); | 145 return new JsonEncoder(toEncodable).convert(value); |
146 } | 146 } |
147 | 147 |
148 JsonEncoder get encoder { | 148 JsonEncoder get encoder { |
149 if (_toEncodable == null) return const JsonEncoder(); | 149 if (_toEncodable == null) return const JsonEncoder(); |
150 return new JsonEncoder(_toEncodable); | 150 return new JsonEncoder(_toEncodable); |
151 } | 151 } |
152 | 152 |
153 JsonDecoder get decoder { | 153 JsonDecoder get decoder { |
154 if (_reviver == null) return const JsonDecoder(); | 154 if (_reviver == null) return const JsonDecoder(); |
155 return new JsonDecoder(_reviver); | 155 return new JsonDecoder(_reviver); |
156 } | 156 } |
157 } | 157 } |
158 | 158 |
159 /** | 159 /** |
160 * This class converts JSON objects to strings. | 160 * This class converts JSON objects to strings. |
161 */ | 161 */ |
162 class JsonEncoder extends ChunkedConverter<Object, String, Object, String> { | 162 class JsonEncoder extends Converter<Object, String> { |
163 /** | 163 /** |
164 * The string used for indention. | 164 * The string used for indention. |
165 * | 165 * |
166 * 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 |
167 * beginning of each indented line for each level of indentation. | 167 * beginning of each indented line for each level of indentation. |
168 * | 168 * |
169 * If `null`, the output is encoded as a single line. | 169 * If `null`, the output is encoded as a single line. |
170 */ | 170 */ |
171 final String indent; | 171 final String indent; |
172 | 172 |
173 /** | 173 /** |
174 * Function called on non-encodable objects to return a replacement | 174 * Function called on non-encodable objects to return a replacement |
175 * encodable object that will be encoded in the orignal's place. | 175 * encodable object that will be encoded in the orignal's place. |
176 */ | 176 */ |
177 final Function _toEncodable; | 177 final _ToEncodable _toEncodable; |
178 | 178 |
179 /** | 179 /** |
180 * Creates a JSON encoder. | 180 * Creates a JSON encoder. |
181 * | 181 * |
182 * The JSON encoder handles numbers, strings, booleans, null, lists and | 182 * The JSON encoder handles numbers, strings, booleans, null, lists and |
183 * maps directly. | 183 * maps directly. |
184 * | 184 * |
185 * Any other object is attempted converted by [toEncodable] to an | 185 * Any other object is attempted converted by [toEncodable] to an |
186 * object that is of one of the convertible types. | 186 * object that is of one of the convertible types. |
187 * | 187 * |
188 * If [toEncodable] is omitted, it defaults to calling `.toJson()` on | 188 * If [toEncodable] is omitted, it defaults to calling `.toJson()` on |
189 * the object. | 189 * the object. |
190 */ | 190 */ |
191 const JsonEncoder([Object toEncodable(Object nonSerializable)]) | 191 const JsonEncoder([toEncodable(nonSerializable)]) |
192 : this.indent = null, | 192 : this.indent = null, |
193 this._toEncodable = toEncodable; | 193 this._toEncodable = toEncodable; |
194 | 194 |
195 /** | 195 /** |
196 * Creates a JSON encoder that creates multi-line JSON. | 196 * Creates a JSON encoder that creates multi-line JSON. |
197 * | 197 * |
198 * The encoding of elements of lists and maps are indented and put on separate | 198 * The encoding of elements of lists and maps are indented and put on separate |
199 * lines. The [indent] string is prepended to these elements, once for each | 199 * lines. The [indent] string is prepended to these elements, once for each |
200 * level of indentation. | 200 * level of indentation. |
201 * | 201 * |
202 * If [indent] is `null`, the output is encoded as a single line. | 202 * If [indent] is `null`, the output is encoded as a single line. |
203 * | 203 * |
204 * The JSON encoder handles numbers, strings, booleans, null, lists and | 204 * The JSON encoder handles numbers, strings, booleans, null, lists and |
205 * maps directly. | 205 * maps directly. |
206 * | 206 * |
207 * Any other object is attempted converted by [toEncodable] to an | 207 * Any other object is attempted converted by [toEncodable] to an |
208 * object that is of one of the convertible types. | 208 * object that is of one of the convertible types. |
209 * | 209 * |
210 * If [toEncodable] is omitted, it defaults to calling `.toJson()` on | 210 * If [toEncodable] is omitted, it defaults to calling `.toJson()` on |
211 * the object. | 211 * the object. |
212 */ | 212 */ |
213 const JsonEncoder.withIndent(this.indent, | 213 const JsonEncoder.withIndent(this.indent, [toEncodable(nonSerializable)]) |
214 [Object toEncodable(Object nonSerializable)]) | |
215 : this._toEncodable = toEncodable; | 214 : this._toEncodable = toEncodable; |
216 | 215 |
217 /** | 216 /** |
218 * Converts [object] to a JSON [String]. | 217 * Converts [object] to a JSON [String]. |
219 * | 218 * |
220 * Directly serializable values are [num], [String], [bool], and [Null], as | 219 * Directly serializable values are [num], [String], [bool], and [Null], as |
221 * well as some [List] and [Map] values. For [List], the elements must all be | 220 * well as some [List] and [Map] values. For [List], the elements must all be |
222 * serializable. For [Map], the keys must be [String] and the values must be | 221 * serializable. For [Map], the keys must be [String] and the values must be |
223 * serializable. | 222 * serializable. |
224 * | 223 * |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
261 return new _JsonUtf8EncoderSink(sink._sink, _toEncodable, | 260 return new _JsonUtf8EncoderSink(sink._sink, _toEncodable, |
262 JsonUtf8Encoder._utf8Encode(indent), | 261 JsonUtf8Encoder._utf8Encode(indent), |
263 JsonUtf8Encoder.DEFAULT_BUFFER_SIZE); | 262 JsonUtf8Encoder.DEFAULT_BUFFER_SIZE); |
264 } | 263 } |
265 return new _JsonEncoderSink(sink, _toEncodable, indent); | 264 return new _JsonEncoderSink(sink, _toEncodable, indent); |
266 } | 265 } |
267 | 266 |
268 // Override the base class's bind, to provide a better type. | 267 // Override the base class's bind, to provide a better type. |
269 Stream<String> bind(Stream<Object> stream) => super.bind(stream); | 268 Stream<String> bind(Stream<Object> stream) => super.bind(stream); |
270 | 269 |
271 Converter<Object, dynamic> fuse(Converter<String, dynamic> other) { | 270 Converter<Object, dynamic/*=T*/> fuse/*<T>*/( |
| 271 Converter<String, dynamic/*=T*/> other) { |
272 if (other is Utf8Encoder) { | 272 if (other is Utf8Encoder) { |
273 return new JsonUtf8Encoder(indent, _toEncodable); | 273 return new JsonUtf8Encoder(indent, _toEncodable) |
| 274 as dynamic/*=Converter<Object, T>*/; |
274 } | 275 } |
275 return super.fuse(other); | 276 return super.fuse/*<T>*/(other); |
276 } | 277 } |
277 } | 278 } |
278 | 279 |
279 /** | 280 /** |
280 * Encoder that encodes a single object as a UTF-8 encoded JSON string. | 281 * Encoder that encodes a single object as a UTF-8 encoded JSON string. |
281 * | 282 * |
282 * This encoder works equivalently to first converting the object to | 283 * This encoder works equivalently to first converting the object to |
283 * a JSON string, and then UTF-8 encoding the string, but without | 284 * a JSON string, and then UTF-8 encoding the string, but without |
284 * creating an intermediate string. | 285 * creating an intermediate string. |
285 */ | 286 */ |
286 class JsonUtf8Encoder extends | 287 class JsonUtf8Encoder extends Converter<Object, List<int>> { |
287 ChunkedConverter<Object, List<int>, Object, List<int>> { | |
288 /** Default buffer size used by the JSON-to-UTF-8 encoder. */ | 288 /** Default buffer size used by the JSON-to-UTF-8 encoder. */ |
289 static const int DEFAULT_BUFFER_SIZE = 256; | 289 static const int DEFAULT_BUFFER_SIZE = 256; |
290 /** Indentation used in pretty-print mode, `null` if not pretty. */ | 290 /** Indentation used in pretty-print mode, `null` if not pretty. */ |
291 final List<int> _indent; | 291 final List<int> _indent; |
292 /** Function called with each un-encodable object encountered. */ | 292 /** Function called with each un-encodable object encountered. */ |
293 final Function _toEncodable; | 293 final _ToEncodable _toEncodable; |
294 /** UTF-8 buffer size. */ | 294 /** UTF-8 buffer size. */ |
295 final int _bufferSize; | 295 final int _bufferSize; |
296 | 296 |
297 /** | 297 /** |
298 * Create converter. | 298 * Create converter. |
299 * | 299 * |
300 * 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 |
301 * 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 |
302 * whitespace outside of string literals. | 302 * whitespace outside of string literals. |
303 * If `indent` contains characters that are not valid JSON whitespace | 303 * If `indent` contains characters that are not valid JSON whitespace |
304 * characters, the result will not be valid JSON. JSON whitespace characters | 304 * characters, the result will not be valid JSON. JSON whitespace characters |
305 * 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 |
306 * (U+000d) ([ECMA | 306 * (U+000d) ([ECMA |
307 * 404](http://www.ecma-international.org/publications/standards/Ecma-404.htm)
). | 307 * 404](http://www.ecma-international.org/publications/standards/Ecma-404.htm)
). |
308 * | 308 * |
309 * 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 |
310 * UTF-8 code units. | 310 * UTF-8 code units. |
311 * If using [startChunkedConversion], it will be the size of the chunks. | 311 * If using [startChunkedConversion], it will be the size of the chunks. |
312 * | 312 * |
313 * The JSON encoder handles numbers, strings, booleans, null, lists and maps | 313 * The JSON encoder handles numbers, strings, booleans, null, lists and maps |
314 * directly. | 314 * directly. |
315 * | 315 * |
316 * Any other object is attempted converted by [toEncodable] to an object that | 316 * Any other object is attempted converted by [toEncodable] to an object that |
317 * is of one of the convertible types. | 317 * is of one of the convertible types. |
318 * | 318 * |
319 * If [toEncodable] is omitted, it defaults to calling `.toJson()` on the | 319 * If [toEncodable] is omitted, it defaults to calling `.toJson()` on the |
320 * object. | 320 * object. |
321 */ | 321 */ |
322 JsonUtf8Encoder([String indent, | 322 JsonUtf8Encoder([String indent, |
323 toEncodable(Object object), | 323 toEncodable(object), |
324 int bufferSize = DEFAULT_BUFFER_SIZE]) | 324 int bufferSize = DEFAULT_BUFFER_SIZE]) |
325 : _indent = _utf8Encode(indent), | 325 : _indent = _utf8Encode(indent), |
326 _toEncodable = toEncodable, | 326 _toEncodable = toEncodable, |
327 _bufferSize = bufferSize; | 327 _bufferSize = bufferSize; |
328 | 328 |
329 static List<int> _utf8Encode(String string) { | 329 static List<int> _utf8Encode(String string) { |
330 if (string == null) return null; | 330 if (string == null) return null; |
331 if (string.isEmpty) return new Uint8List(0); | 331 if (string.isEmpty) return new Uint8List(0); |
332 checkAscii: { | 332 checkAscii: { |
333 for (int i = 0; i < string.length; i++) { | 333 for (int i = 0; i < string.length; i++) { |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
388 byteSink = new ByteConversionSink.from(sink); | 388 byteSink = new ByteConversionSink.from(sink); |
389 } | 389 } |
390 return new _JsonUtf8EncoderSink(byteSink, _toEncodable, | 390 return new _JsonUtf8EncoderSink(byteSink, _toEncodable, |
391 _indent, _bufferSize); | 391 _indent, _bufferSize); |
392 } | 392 } |
393 | 393 |
394 // Override the base class's bind, to provide a better type. | 394 // Override the base class's bind, to provide a better type. |
395 Stream<List<int>> bind(Stream<Object> stream) { | 395 Stream<List<int>> bind(Stream<Object> stream) { |
396 return super.bind(stream); | 396 return super.bind(stream); |
397 } | 397 } |
398 | |
399 Converter<Object, dynamic> fuse(Converter<List<int>, dynamic> other) { | |
400 return super.fuse(other); | |
401 } | |
402 } | 398 } |
403 | 399 |
404 /** | 400 /** |
405 * Implements the chunked conversion from object to its JSON representation. | 401 * Implements the chunked conversion from object to its JSON representation. |
406 * | 402 * |
407 * The sink only accepts one value, but will produce output in a chunked way. | 403 * The sink only accepts one value, but will produce output in a chunked way. |
408 */ | 404 */ |
409 class _JsonEncoderSink extends ChunkedConversionSink<Object> { | 405 class _JsonEncoderSink extends ChunkedConversionSink<Object> { |
410 final String _indent; | 406 final String _indent; |
411 final Function _toEncodable; | 407 final _ToEncodable _toEncodable; |
412 final StringConversionSink _sink; | 408 final StringConversionSink _sink; |
413 bool _isDone = false; | 409 bool _isDone = false; |
414 | 410 |
415 _JsonEncoderSink(this._sink, this._toEncodable, this._indent); | 411 _JsonEncoderSink(this._sink, this._toEncodable, this._indent); |
416 | 412 |
417 /** | 413 /** |
418 * Encodes the given object [o]. | 414 * Encodes the given object [o]. |
419 * | 415 * |
420 * It is an error to invoke this method more than once on any instance. While | 416 * It is an error to invoke this method more than once on any instance. While |
421 * this makes the input effectly non-chunked the output will be generated in | 417 * this makes the input effectly non-chunked the output will be generated in |
(...skipping 12 matching lines...) Expand all Loading... |
434 void close() { /* do nothing */ } | 430 void close() { /* do nothing */ } |
435 } | 431 } |
436 | 432 |
437 /** | 433 /** |
438 * Sink returned when starting a chunked conversion from object to bytes. | 434 * Sink returned when starting a chunked conversion from object to bytes. |
439 */ | 435 */ |
440 class _JsonUtf8EncoderSink extends ChunkedConversionSink<Object> { | 436 class _JsonUtf8EncoderSink extends ChunkedConversionSink<Object> { |
441 /** The byte sink receiveing the encoded chunks. */ | 437 /** The byte sink receiveing the encoded chunks. */ |
442 final ByteConversionSink _sink; | 438 final ByteConversionSink _sink; |
443 final List<int> _indent; | 439 final List<int> _indent; |
444 final Function _toEncodable; | 440 final _ToEncodable _toEncodable; |
445 final int _bufferSize; | 441 final int _bufferSize; |
446 bool _isDone = false; | 442 bool _isDone = false; |
447 _JsonUtf8EncoderSink(this._sink, this._toEncodable, this._indent, | 443 _JsonUtf8EncoderSink(this._sink, this._toEncodable, this._indent, |
448 this._bufferSize); | 444 this._bufferSize); |
449 | 445 |
450 /** Callback called for each slice of result bytes. */ | 446 /** Callback called for each slice of result bytes. */ |
451 void _addChunk(Uint8List chunk, int start, int end) { | 447 void _addChunk(Uint8List chunk, int start, int end) { |
452 _sink.addSlice(chunk, start, end, false); | 448 _sink.addSlice(chunk, start, end, false); |
453 } | 449 } |
454 | 450 |
(...skipping 12 matching lines...) Expand all Loading... |
467 if (!_isDone) { | 463 if (!_isDone) { |
468 _isDone = true; | 464 _isDone = true; |
469 _sink.close(); | 465 _sink.close(); |
470 } | 466 } |
471 } | 467 } |
472 } | 468 } |
473 | 469 |
474 /** | 470 /** |
475 * This class parses JSON strings and builds the corresponding objects. | 471 * This class parses JSON strings and builds the corresponding objects. |
476 */ | 472 */ |
477 class JsonDecoder extends ChunkedConverter<String, Object, String, Object> { | 473 class JsonDecoder extends Converter<String, Object> { |
478 final _Reviver _reviver; | 474 final _Reviver _reviver; |
479 /** | 475 /** |
480 * Constructs a new JsonDecoder. | 476 * Constructs a new JsonDecoder. |
481 * | 477 * |
482 * The [reviver] may be `null`. | 478 * The [reviver] may be `null`. |
483 */ | 479 */ |
484 const JsonDecoder([reviver(var key, var value)]) : this._reviver = reviver; | 480 const JsonDecoder([reviver(var key, var value)]) : this._reviver = reviver; |
485 | 481 |
486 /** | 482 /** |
487 * Converts the given JSON-string [input] to its corresponding object. | 483 * Converts the given JSON-string [input] to its corresponding object. |
(...skipping 22 matching lines...) Expand all Loading... |
510 // Override the base class's bind, to provide a better type. | 506 // Override the base class's bind, to provide a better type. |
511 Stream<Object> bind(Stream<String> stream) => super.bind(stream); | 507 Stream<Object> bind(Stream<String> stream) => super.bind(stream); |
512 } | 508 } |
513 | 509 |
514 // Internal optimized JSON parsing implementation. | 510 // Internal optimized JSON parsing implementation. |
515 external _parseJson(String source, reviver(key, value)); | 511 external _parseJson(String source, reviver(key, value)); |
516 | 512 |
517 | 513 |
518 // Implementation of encoder/stringifier. | 514 // Implementation of encoder/stringifier. |
519 | 515 |
520 Object _defaultToEncodable(object) => object.toJson(); | 516 dynamic _defaultToEncodable(dynamic object) => object.toJson(); |
521 | 517 |
522 /** | 518 /** |
523 * JSON encoder that traverses an object structure and writes JSON source. | 519 * JSON encoder that traverses an object structure and writes JSON source. |
524 * | 520 * |
525 * This is an abstract implementation that doesn't decide on the output | 521 * This is an abstract implementation that doesn't decide on the output |
526 * format, but writes the JSON through abstract methods like [writeString]. | 522 * format, but writes the JSON through abstract methods like [writeString]. |
527 */ | 523 */ |
528 abstract class _JsonStringifier { | 524 abstract class _JsonStringifier { |
529 // Character code constants. | 525 // Character code constants. |
530 static const int BACKSPACE = 0x08; | 526 static const int BACKSPACE = 0x08; |
531 static const int TAB = 0x09; | 527 static const int TAB = 0x09; |
532 static const int NEWLINE = 0x0a; | 528 static const int NEWLINE = 0x0a; |
533 static const int CARRIAGE_RETURN = 0x0d; | 529 static const int CARRIAGE_RETURN = 0x0d; |
534 static const int FORM_FEED = 0x0c; | 530 static const int FORM_FEED = 0x0c; |
535 static const int QUOTE = 0x22; | 531 static const int QUOTE = 0x22; |
536 static const int CHAR_0 = 0x30; | 532 static const int CHAR_0 = 0x30; |
537 static const int BACKSLASH = 0x5c; | 533 static const int BACKSLASH = 0x5c; |
538 static const int CHAR_b = 0x62; | 534 static const int CHAR_b = 0x62; |
539 static const int CHAR_f = 0x66; | 535 static const int CHAR_f = 0x66; |
540 static const int CHAR_n = 0x6e; | 536 static const int CHAR_n = 0x6e; |
541 static const int CHAR_r = 0x72; | 537 static const int CHAR_r = 0x72; |
542 static const int CHAR_t = 0x74; | 538 static const int CHAR_t = 0x74; |
543 static const int CHAR_u = 0x75; | 539 static const int CHAR_u = 0x75; |
544 | 540 |
545 /** List of objects currently being traversed. Used to detect cycles. */ | 541 /** List of objects currently being traversed. Used to detect cycles. */ |
546 final List _seen = new List(); | 542 final List _seen = new List(); |
547 /** Function called for each un-encodable object encountered. */ | 543 /** Function called for each un-encodable object encountered. */ |
548 final Function _toEncodable; | 544 final _ToEncodable _toEncodable; |
549 | 545 |
550 _JsonStringifier(Object _toEncodable(Object o)) | 546 _JsonStringifier(toEncodable(o)) |
551 : _toEncodable = (_toEncodable != null) ? _toEncodable | 547 : _toEncodable = toEncodable ?? _defaultToEncodable; |
552 : _defaultToEncodable; | |
553 | 548 |
554 /** Append a string to the JSON output. */ | 549 /** Append a string to the JSON output. */ |
555 void writeString(String characters); | 550 void writeString(String characters); |
556 /** Append part of a string to the JSON output. */ | 551 /** Append part of a string to the JSON output. */ |
557 void writeStringSlice(String characters, int start, int end); | 552 void writeStringSlice(String characters, int start, int end); |
558 /** Append a single character, given by its code point, to the JSON output. */ | 553 /** Append a single character, given by its code point, to the JSON output. */ |
559 void writeCharCode(int charCode); | 554 void writeCharCode(int charCode); |
560 /** Write a number to the JSON output. */ | 555 /** Write a number to the JSON output. */ |
561 void writeNumber(num number); | 556 void writeNumber(num number); |
562 | 557 |
(...skipping 149 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
712 writeObject(list[0]); | 707 writeObject(list[0]); |
713 for (int i = 1; i < list.length; i++) { | 708 for (int i = 1; i < list.length; i++) { |
714 writeString(','); | 709 writeString(','); |
715 writeObject(list[i]); | 710 writeObject(list[i]); |
716 } | 711 } |
717 } | 712 } |
718 writeString(']'); | 713 writeString(']'); |
719 } | 714 } |
720 | 715 |
721 /** Serialize a [Map]. */ | 716 /** Serialize a [Map]. */ |
722 bool writeMap(Map<String, Object> map) { | 717 bool writeMap(Map map) { |
723 if (map.isEmpty) { | 718 if (map.isEmpty) { |
724 writeString("{}"); | 719 writeString("{}"); |
725 return true; | 720 return true; |
726 } | 721 } |
727 List keyValueList = new List(map.length * 2); | 722 List keyValueList = new List(map.length * 2); |
728 int i = 0; | 723 int i = 0; |
729 bool allStringKeys = true; | 724 bool allStringKeys = true; |
730 map.forEach((key, value) { | 725 map.forEach((key, value) { |
731 if (key is! String) { | 726 if (key is! String) { |
732 allStringKeys = false; | 727 allStringKeys = false; |
(...skipping 21 matching lines...) Expand all Loading... |
754 * [Map] objects using the specified indent value. | 749 * [Map] objects using the specified indent value. |
755 * | 750 * |
756 * Subclasses should implement [writeIndentation]. | 751 * Subclasses should implement [writeIndentation]. |
757 */ | 752 */ |
758 abstract class _JsonPrettyPrintMixin implements _JsonStringifier { | 753 abstract class _JsonPrettyPrintMixin implements _JsonStringifier { |
759 int _indentLevel = 0; | 754 int _indentLevel = 0; |
760 | 755 |
761 /** | 756 /** |
762 * Add [indentLevel] indentations to the JSON output. | 757 * Add [indentLevel] indentations to the JSON output. |
763 */ | 758 */ |
764 void writeIndentation(indentLevel); | 759 void writeIndentation(int indentLevel); |
765 | 760 |
766 void writeList(List list) { | 761 void writeList(List list) { |
767 if (list.isEmpty) { | 762 if (list.isEmpty) { |
768 writeString('[]'); | 763 writeString('[]'); |
769 } else { | 764 } else { |
770 writeString('[\n'); | 765 writeString('[\n'); |
771 _indentLevel++; | 766 _indentLevel++; |
772 writeIndentation(_indentLevel); | 767 writeIndentation(_indentLevel); |
773 writeObject(list[0]); | 768 writeObject(list[0]); |
774 for (int i = 1; i < list.length; i++) { | 769 for (int i = 1; i < list.length; i++) { |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
831 * Convert object to a string. | 826 * Convert object to a string. |
832 * | 827 * |
833 * The [toEncodable] function is used to convert non-encodable objects | 828 * The [toEncodable] function is used to convert non-encodable objects |
834 * to encodable ones. | 829 * to encodable ones. |
835 * | 830 * |
836 * If [indent] is not `null`, the resulting JSON will be "pretty-printed" | 831 * If [indent] is not `null`, the resulting JSON will be "pretty-printed" |
837 * with newlines and indentation. The `indent` string is added as indentation | 832 * with newlines and indentation. The `indent` string is added as indentation |
838 * for each indentation level. It should only contain valid JSON whitespace | 833 * for each indentation level. It should only contain valid JSON whitespace |
839 * characters (space, tab, carriage return or line feed). | 834 * characters (space, tab, carriage return or line feed). |
840 */ | 835 */ |
841 static String stringify(object, toEncodable(object), String indent) { | 836 static String stringify(object, toEncodable(o), String indent) { |
842 StringBuffer output = new StringBuffer(); | 837 StringBuffer output = new StringBuffer(); |
843 printOn(object, output, toEncodable, indent); | 838 printOn(object, output, toEncodable, indent); |
844 return output.toString(); | 839 return output.toString(); |
845 } | 840 } |
846 | 841 |
847 /** | 842 /** |
848 * Convert object to a string, and write the result to the [output] sink. | 843 * Convert object to a string, and write the result to the [output] sink. |
849 * | 844 * |
850 * The result is written piecemally to the sink. | 845 * The result is written piecemally to the sink. |
851 */ | 846 */ |
852 static void printOn(object, StringSink output, toEncodable(object), | 847 static void printOn( |
853 String indent) { | 848 object, StringSink output, toEncodable(o), String indent) { |
854 var stringifier; | 849 var stringifier; |
855 if (indent == null) { | 850 if (indent == null) { |
856 stringifier = new _JsonStringStringifier(output, toEncodable); | 851 stringifier = new _JsonStringStringifier(output, toEncodable); |
857 } else { | 852 } else { |
858 stringifier = | 853 stringifier = |
859 new _JsonStringStringifierPretty(output, toEncodable, indent); | 854 new _JsonStringStringifierPretty(output, toEncodable, indent); |
860 } | 855 } |
861 stringifier.writeObject(object); | 856 stringifier.writeObject(object); |
862 } | 857 } |
863 | 858 |
864 void writeNumber(num number) { | 859 void writeNumber(num number) { |
865 _sink.write(number.toString()); | 860 _sink.write(number.toString()); |
866 } | 861 } |
867 void writeString(String string) { | 862 void writeString(String string) { |
868 _sink.write(string); | 863 _sink.write(string); |
869 } | 864 } |
870 void writeStringSlice(String string, int start, int end) { | 865 void writeStringSlice(String string, int start, int end) { |
871 _sink.write(string.substring(start, end)); | 866 _sink.write(string.substring(start, end)); |
872 } | 867 } |
873 void writeCharCode(int charCode) { | 868 void writeCharCode(int charCode) { |
874 _sink.writeCharCode(charCode); | 869 _sink.writeCharCode(charCode); |
875 } | 870 } |
876 } | 871 } |
877 | 872 |
878 class _JsonStringStringifierPretty extends _JsonStringStringifier | 873 class _JsonStringStringifierPretty extends _JsonStringStringifier |
879 with _JsonPrettyPrintMixin { | 874 with _JsonPrettyPrintMixin { |
880 final String _indent; | 875 final String _indent; |
881 | 876 |
882 _JsonStringStringifierPretty(StringSink sink, Function toEncodable, | 877 _JsonStringStringifierPretty(StringSink sink, toEncodable(o), this._indent) |
883 this._indent) | |
884 : super(sink, toEncodable); | 878 : super(sink, toEncodable); |
885 | 879 |
886 void writeIndentation(int count) { | 880 void writeIndentation(int count) { |
887 for (int i = 0; i < count; i++) writeString(_indent); | 881 for (int i = 0; i < count; i++) writeString(_indent); |
888 } | 882 } |
889 } | 883 } |
890 | 884 |
| 885 typedef void _AddChunk(Uint8List list, int start, int end); |
| 886 |
891 /** | 887 /** |
892 * Specialization of [_JsonStringifier] that writes the JSON as UTF-8. | 888 * Specialization of [_JsonStringifier] that writes the JSON as UTF-8. |
893 * | 889 * |
894 * The JSON text is UTF-8 encoded and written to [Uint8List] buffers. | 890 * The JSON text is UTF-8 encoded and written to [Uint8List] buffers. |
895 * The buffers are then passed back to a user provided callback method. | 891 * The buffers are then passed back to a user provided callback method. |
896 */ | 892 */ |
897 class _JsonUtf8Stringifier extends _JsonStringifier { | 893 class _JsonUtf8Stringifier extends _JsonStringifier { |
898 final int bufferSize; | 894 final int bufferSize; |
899 final Function addChunk; | 895 final _AddChunk addChunk; |
900 Uint8List buffer; | 896 Uint8List buffer; |
901 int index = 0; | 897 int index = 0; |
902 | 898 |
903 _JsonUtf8Stringifier(toEncodable, int bufferSize, this.addChunk) | 899 _JsonUtf8Stringifier(toEncodable(o), int bufferSize, this.addChunk) |
904 : this.bufferSize = bufferSize, | 900 : this.bufferSize = bufferSize, |
905 buffer = new Uint8List(bufferSize), | 901 buffer = new Uint8List(bufferSize), |
906 super(toEncodable); | 902 super(toEncodable); |
907 | 903 |
908 /** | 904 /** |
909 * Convert [object] to UTF-8 encoded JSON. | 905 * Convert [object] to UTF-8 encoded JSON. |
910 * | 906 * |
911 * Calls [addChunk] with slices of UTF-8 code units. | 907 * Calls [addChunk] with slices of UTF-8 code units. |
912 * These will typically have size [bufferSize], but may be shorter. | 908 * These will typically have size [bufferSize], but may be shorter. |
913 * The buffers are not reused, so the [addChunk] call may keep and reuse the | 909 * The buffers are not reused, so the [addChunk] call may keep and reuse the |
914 * chunks. | 910 * chunks. |
915 * | 911 * |
916 * If [indent] is non-`null`, the result will be "pretty-printed" with extra | 912 * If [indent] is non-`null`, the result will be "pretty-printed" with extra |
917 * newlines and indentation, using [indent] as the indentation. | 913 * newlines and indentation, using [indent] as the indentation. |
918 */ | 914 */ |
919 static void stringify(Object object, | 915 static void stringify(Object object, |
920 List<int> indent, | 916 List<int> indent, |
921 toEncodableFunction(Object o), | 917 toEncodable(o), |
922 int bufferSize, | 918 int bufferSize, |
923 void addChunk(Uint8List chunk, int start, int end)) { | 919 void addChunk(Uint8List chunk, int start, int end)) { |
924 _JsonUtf8Stringifier stringifier; | 920 _JsonUtf8Stringifier stringifier; |
925 if (indent != null) { | 921 if (indent != null) { |
926 stringifier = new _JsonUtf8StringifierPretty(toEncodableFunction, indent, | 922 stringifier = new _JsonUtf8StringifierPretty(toEncodable, indent, |
927 bufferSize, addChunk); | 923 bufferSize, addChunk); |
928 } else { | 924 } else { |
929 stringifier = new _JsonUtf8Stringifier(toEncodableFunction, | 925 stringifier = new _JsonUtf8Stringifier(toEncodable, bufferSize, addChunk); |
930 bufferSize, addChunk); | |
931 } | 926 } |
932 stringifier.writeObject(object); | 927 stringifier.writeObject(object); |
933 stringifier.flush(); | 928 stringifier.flush(); |
934 } | 929 } |
935 | 930 |
936 /** | 931 /** |
937 * Must be called at the end to push the last chunk to the [addChunk] | 932 * Must be called at the end to push the last chunk to the [addChunk] |
938 * callback. | 933 * callback. |
939 */ | 934 */ |
940 void flush() { | 935 void flush() { |
(...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1030 buffer[index++] = byte; | 1025 buffer[index++] = byte; |
1031 } | 1026 } |
1032 } | 1027 } |
1033 | 1028 |
1034 /** | 1029 /** |
1035 * Pretty-printing version of [_JsonUtf8Stringifier]. | 1030 * Pretty-printing version of [_JsonUtf8Stringifier]. |
1036 */ | 1031 */ |
1037 class _JsonUtf8StringifierPretty extends _JsonUtf8Stringifier | 1032 class _JsonUtf8StringifierPretty extends _JsonUtf8Stringifier |
1038 with _JsonPrettyPrintMixin { | 1033 with _JsonPrettyPrintMixin { |
1039 final List<int> indent; | 1034 final List<int> indent; |
1040 _JsonUtf8StringifierPretty(toEncodableFunction, this.indent, | 1035 _JsonUtf8StringifierPretty( |
1041 bufferSize, addChunk) | 1036 toEncodable(o), this.indent, |
1042 : super(toEncodableFunction, bufferSize, addChunk); | 1037 bufferSize, void addChunk(Uint8List buffer, int start, int end)) |
| 1038 : super(toEncodable, bufferSize, addChunk); |
1043 | 1039 |
1044 void writeIndentation(int count) { | 1040 void writeIndentation(int count) { |
1045 List<int> indent = this.indent; | 1041 List<int> indent = this.indent; |
1046 int indentLength = indent.length; | 1042 int indentLength = indent.length; |
1047 if (indentLength == 1) { | 1043 if (indentLength == 1) { |
1048 int char = indent[0]; | 1044 int char = indent[0]; |
1049 while (count > 0) { | 1045 while (count > 0) { |
1050 writeByte(char); | 1046 writeByte(char); |
1051 count -= 1; | 1047 count -= 1; |
1052 } | 1048 } |
1053 return; | 1049 return; |
1054 } | 1050 } |
1055 while (count > 0) { | 1051 while (count > 0) { |
1056 count--; | 1052 count--; |
1057 int end = index + indentLength; | 1053 int end = index + indentLength; |
1058 if (end <= buffer.length) { | 1054 if (end <= buffer.length) { |
1059 buffer.setRange(index, end, indent); | 1055 buffer.setRange(index, end, indent); |
1060 index = end; | 1056 index = end; |
1061 } else { | 1057 } else { |
1062 for (int i = 0; i < indentLength; i++) { | 1058 for (int i = 0; i < indentLength; i++) { |
1063 writeByte(indent[i]); | 1059 writeByte(indent[i]); |
1064 } | 1060 } |
1065 } | 1061 } |
1066 } | 1062 } |
1067 } | 1063 } |
1068 } | 1064 } |
OLD | NEW |