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. |
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 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() { |
26 if (cause != null) { | 26 if (cause != null) { |
27 return "Converting object to an encodable object failed."; | 27 return "Converting object to an encodable object failed."; |
28 } else { | 28 } else { |
29 return "Converting object did not return an encodable object."; | 29 return "Converting object did not return an encodable object."; |
30 } | 30 } |
31 } | 31 } |
32 } | 32 } |
33 | 33 |
34 | |
35 /** | 34 /** |
36 * Reports that an object could not be stringified due to cyclic references. | 35 * Reports that an object could not be stringified due to cyclic references. |
37 * | 36 * |
38 * An object that references itself cannot be serialized by [stringify]. | 37 * An object that references itself cannot be serialized by [stringify]. |
39 * When the cycle is detected, a [JsonCyclicError] is thrown. | 38 * When the cycle is detected, a [JsonCyclicError] is thrown. |
40 */ | 39 */ |
41 class JsonCyclicError extends JsonUnsupportedObjectError { | 40 class JsonCyclicError extends JsonUnsupportedObjectError { |
42 /** The first object that was detected as part of a cycle. */ | 41 /** The first object that was detected as part of a cycle. */ |
43 JsonCyclicError(Object object): super(object); | 42 JsonCyclicError(Object object) : super(object); |
44 String toString() => "Cyclic error in JSON stringify"; | 43 String toString() => "Cyclic error in JSON stringify"; |
45 } | 44 } |
46 | 45 |
47 | |
48 /** | 46 /** |
49 * An instance of the default implementation of the [JsonCodec]. | 47 * An instance of the default implementation of the [JsonCodec]. |
50 * | 48 * |
51 * This instance provides a convenient access to the most common JSON | 49 * This instance provides a convenient access to the most common JSON |
52 * use cases. | 50 * use cases. |
53 * | 51 * |
54 * Examples: | 52 * Examples: |
55 * | 53 * |
56 * var encoded = JSON.encode([1, 2, { "a": null }]); | 54 * var encoded = JSON.encode([1, 2, { "a": null }]); |
57 * var decoded = JSON.decode('["foo", { "bar": 499 }]'); | 55 * var decoded = JSON.decode('["foo", { "bar": 499 }]'); |
58 */ | 56 */ |
59 const JsonCodec JSON = const JsonCodec(); | 57 const JsonCodec JSON = const JsonCodec(); |
60 | 58 |
61 typedef _Reviver(var key, var value); | 59 typedef _Reviver(var key, var value); |
62 typedef _ToEncodable(var o); | 60 typedef _ToEncodable(var o); |
63 | 61 |
64 | |
65 /** | 62 /** |
66 * A [JsonCodec] encodes JSON objects to strings and decodes strings to | 63 * A [JsonCodec] encodes JSON objects to strings and decodes strings to |
67 * JSON objects. | 64 * JSON objects. |
68 * | 65 * |
69 * Examples: | 66 * Examples: |
70 * | 67 * |
71 * var encoded = JSON.encode([1, 2, { "a": null }]); | 68 * var encoded = JSON.encode([1, 2, { "a": null }]); |
72 * var decoded = JSON.decode('["foo", { "bar": 499 }]'); | 69 * var decoded = JSON.decode('["foo", { "bar": 499 }]'); |
73 */ | 70 */ |
74 class JsonCodec extends Codec<Object, String> { | 71 class JsonCodec extends Codec<Object, String> { |
(...skipping 176 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
251 * The converter works more efficiently if the given [sink] is a | 248 * The converter works more efficiently if the given [sink] is a |
252 * [StringConversionSink]. | 249 * [StringConversionSink]. |
253 * | 250 * |
254 * Returns a chunked-conversion sink that accepts at most one object. It is | 251 * Returns a chunked-conversion sink that accepts at most one object. It is |
255 * an error to invoke `add` more than once on the returned sink. | 252 * an error to invoke `add` more than once on the returned sink. |
256 */ | 253 */ |
257 ChunkedConversionSink<Object> startChunkedConversion(Sink<String> sink) { | 254 ChunkedConversionSink<Object> startChunkedConversion(Sink<String> sink) { |
258 if (sink is! StringConversionSink) { | 255 if (sink is! StringConversionSink) { |
259 sink = new StringConversionSink.from(sink); | 256 sink = new StringConversionSink.from(sink); |
260 } else if (sink is _Utf8EncoderSink) { | 257 } else if (sink is _Utf8EncoderSink) { |
261 return new _JsonUtf8EncoderSink(sink._sink, _toEncodable, | 258 return new _JsonUtf8EncoderSink( |
262 JsonUtf8Encoder._utf8Encode(indent), | 259 sink._sink, |
263 JsonUtf8Encoder.DEFAULT_BUFFER_SIZE); | 260 _toEncodable, |
| 261 JsonUtf8Encoder._utf8Encode(indent), |
| 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, T> fuse<T>(Converter<String, T> other) { | 270 Converter<Object, T> fuse<T>(Converter<String, T> other) { |
272 if (other is Utf8Encoder) { | 271 if (other is Utf8Encoder) { |
273 return new JsonUtf8Encoder(indent, _toEncodable) | 272 return new JsonUtf8Encoder(indent, _toEncodable) |
274 as dynamic/*=Converter<Object, T>*/; | 273 as dynamic/*=Converter<Object, T>*/; |
275 } | 274 } |
276 return super.fuse<T>(other); | 275 return super.fuse<T>(other); |
277 } | 276 } |
278 } | 277 } |
279 | 278 |
280 /** | 279 /** |
281 * 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. |
282 * | 281 * |
283 * This encoder works equivalently to first converting the object to | 282 * This encoder works equivalently to first converting the object to |
284 * 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 |
285 * creating an intermediate string. | 284 * creating an intermediate string. |
286 */ | 285 */ |
287 class JsonUtf8Encoder extends Converter<Object, List<int>> | 286 class JsonUtf8Encoder extends Converter<Object, List<int>> |
288 implements ChunkedConverter<Object, List<int>, Object, List<int>> { | 287 implements ChunkedConverter<Object, List<int>, Object, List<int>> { |
289 | |
290 /** Default buffer size used by the JSON-to-UTF-8 encoder. */ | 288 /** Default buffer size used by the JSON-to-UTF-8 encoder. */ |
291 static const int DEFAULT_BUFFER_SIZE = 256; | 289 static const int DEFAULT_BUFFER_SIZE = 256; |
292 /** Indentation used in pretty-print mode, `null` if not pretty. */ | 290 /** Indentation used in pretty-print mode, `null` if not pretty. */ |
293 final List<int> _indent; | 291 final List<int> _indent; |
294 /** Function called with each un-encodable object encountered. */ | 292 /** Function called with each un-encodable object encountered. */ |
295 final _ToEncodable _toEncodable; | 293 final _ToEncodable _toEncodable; |
296 /** UTF-8 buffer size. */ | 294 /** UTF-8 buffer size. */ |
297 final int _bufferSize; | 295 final int _bufferSize; |
298 | 296 |
299 /** | 297 /** |
(...skipping 14 matching lines...) Expand all Loading... |
314 * | 312 * |
315 * The JSON encoder handles numbers, strings, booleans, null, lists and maps | 313 * The JSON encoder handles numbers, strings, booleans, null, lists and maps |
316 * directly. | 314 * directly. |
317 * | 315 * |
318 * 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 |
319 * is of one of the convertible types. | 317 * is of one of the convertible types. |
320 * | 318 * |
321 * If [toEncodable] is omitted, it defaults to calling `.toJson()` on the | 319 * If [toEncodable] is omitted, it defaults to calling `.toJson()` on the |
322 * object. | 320 * object. |
323 */ | 321 */ |
324 JsonUtf8Encoder([String indent, | 322 JsonUtf8Encoder( |
325 toEncodable(object), | 323 [String indent, |
326 int bufferSize = DEFAULT_BUFFER_SIZE]) | 324 toEncodable(object), |
| 325 int bufferSize = DEFAULT_BUFFER_SIZE]) |
327 : _indent = _utf8Encode(indent), | 326 : _indent = _utf8Encode(indent), |
328 _toEncodable = toEncodable, | 327 _toEncodable = toEncodable, |
329 _bufferSize = bufferSize; | 328 _bufferSize = bufferSize; |
330 | 329 |
331 static List<int> _utf8Encode(String string) { | 330 static List<int> _utf8Encode(String string) { |
332 if (string == null) return null; | 331 if (string == null) return null; |
333 if (string.isEmpty) return new Uint8List(0); | 332 if (string.isEmpty) return new Uint8List(0); |
334 checkAscii: { | 333 checkAscii: |
| 334 { |
335 for (int i = 0; i < string.length; i++) { | 335 for (int i = 0; i < string.length; i++) { |
336 if (string.codeUnitAt(i) >= 0x80) break checkAscii; | 336 if (string.codeUnitAt(i) >= 0x80) break checkAscii; |
337 } | 337 } |
338 return string.codeUnits; | 338 return string.codeUnits; |
339 } | 339 } |
340 return UTF8.encode(string); | 340 return UTF8.encode(string); |
341 } | 341 } |
342 | 342 |
343 /** Convert [object] into UTF-8 encoded JSON. */ | 343 /** Convert [object] into UTF-8 encoded JSON. */ |
344 List<int> convert(Object object) { | 344 List<int> convert(Object object) { |
345 List<List<int>> bytes = []; | 345 List<List<int>> bytes = []; |
346 // The `stringify` function always converts into chunks. | 346 // The `stringify` function always converts into chunks. |
347 // Collect the chunks into the `bytes` list, then combine them afterwards. | 347 // Collect the chunks into the `bytes` list, then combine them afterwards. |
348 void addChunk(Uint8List chunk, int start, int end) { | 348 void addChunk(Uint8List chunk, int start, int end) { |
349 if (start > 0 || end < chunk.length) { | 349 if (start > 0 || end < chunk.length) { |
350 int length = end - start; | 350 int length = end - start; |
351 chunk = new Uint8List.view(chunk.buffer, | 351 chunk = new Uint8List.view( |
352 chunk.offsetInBytes + start, | 352 chunk.buffer, chunk.offsetInBytes + start, length); |
353 length); | |
354 } | 353 } |
355 bytes.add(chunk); | 354 bytes.add(chunk); |
356 } | 355 } |
357 _JsonUtf8Stringifier.stringify(object, | 356 |
358 _indent, | 357 _JsonUtf8Stringifier.stringify( |
359 _toEncodable, | 358 object, _indent, _toEncodable, _bufferSize, addChunk); |
360 _bufferSize, | |
361 addChunk); | |
362 if (bytes.length == 1) return bytes[0]; | 359 if (bytes.length == 1) return bytes[0]; |
363 int length = 0; | 360 int length = 0; |
364 for (int i = 0; i < bytes.length; i++) { | 361 for (int i = 0; i < bytes.length; i++) { |
365 length += bytes[i].length; | 362 length += bytes[i].length; |
366 } | 363 } |
367 Uint8List result = new Uint8List(length); | 364 Uint8List result = new Uint8List(length); |
368 for (int i = 0, offset = 0; i < bytes.length; i++) { | 365 for (int i = 0, offset = 0; i < bytes.length; i++) { |
369 var byteList = bytes[i]; | 366 var byteList = bytes[i]; |
370 int end = offset + byteList.length; | 367 int end = offset + byteList.length; |
371 result.setRange(offset, end, byteList); | 368 result.setRange(offset, end, byteList); |
(...skipping 10 matching lines...) Expand all Loading... |
382 * The argument [sink] will receive byte lists in sizes depending on the | 379 * The argument [sink] will receive byte lists in sizes depending on the |
383 * `bufferSize` passed to the constructor when creating this encoder. | 380 * `bufferSize` passed to the constructor when creating this encoder. |
384 */ | 381 */ |
385 ChunkedConversionSink<Object> startChunkedConversion(Sink<List<int>> sink) { | 382 ChunkedConversionSink<Object> startChunkedConversion(Sink<List<int>> sink) { |
386 ByteConversionSink byteSink; | 383 ByteConversionSink byteSink; |
387 if (sink is ByteConversionSink) { | 384 if (sink is ByteConversionSink) { |
388 byteSink = sink; | 385 byteSink = sink; |
389 } else { | 386 } else { |
390 byteSink = new ByteConversionSink.from(sink); | 387 byteSink = new ByteConversionSink.from(sink); |
391 } | 388 } |
392 return new _JsonUtf8EncoderSink(byteSink, _toEncodable, | 389 return new _JsonUtf8EncoderSink( |
393 _indent, _bufferSize); | 390 byteSink, _toEncodable, _indent, _bufferSize); |
394 } | 391 } |
395 | 392 |
396 // Override the base class's bind, to provide a better type. | 393 // Override the base class's bind, to provide a better type. |
397 Stream<List<int>> bind(Stream<Object> stream) { | 394 Stream<List<int>> bind(Stream<Object> stream) { |
398 return super.bind(stream); | 395 return super.bind(stream); |
399 } | 396 } |
400 } | 397 } |
401 | 398 |
402 /** | 399 /** |
403 * Implements the chunked conversion from object to its JSON representation. | 400 * Implements the chunked conversion from object to its JSON representation. |
(...skipping 18 matching lines...) Expand all Loading... |
422 void add(Object o) { | 419 void add(Object o) { |
423 if (_isDone) { | 420 if (_isDone) { |
424 throw new StateError("Only one call to add allowed"); | 421 throw new StateError("Only one call to add allowed"); |
425 } | 422 } |
426 _isDone = true; | 423 _isDone = true; |
427 ClosableStringSink stringSink = _sink.asStringSink(); | 424 ClosableStringSink stringSink = _sink.asStringSink(); |
428 _JsonStringStringifier.printOn(o, stringSink, _toEncodable, _indent); | 425 _JsonStringStringifier.printOn(o, stringSink, _toEncodable, _indent); |
429 stringSink.close(); | 426 stringSink.close(); |
430 } | 427 } |
431 | 428 |
432 void close() { /* do nothing */ } | 429 void close() {/* do nothing */} |
433 } | 430 } |
434 | 431 |
435 /** | 432 /** |
436 * Sink returned when starting a chunked conversion from object to bytes. | 433 * Sink returned when starting a chunked conversion from object to bytes. |
437 */ | 434 */ |
438 class _JsonUtf8EncoderSink extends ChunkedConversionSink<Object> { | 435 class _JsonUtf8EncoderSink extends ChunkedConversionSink<Object> { |
439 /** The byte sink receiveing the encoded chunks. */ | 436 /** The byte sink receiveing the encoded chunks. */ |
440 final ByteConversionSink _sink; | 437 final ByteConversionSink _sink; |
441 final List<int> _indent; | 438 final List<int> _indent; |
442 final _ToEncodable _toEncodable; | 439 final _ToEncodable _toEncodable; |
443 final int _bufferSize; | 440 final int _bufferSize; |
444 bool _isDone = false; | 441 bool _isDone = false; |
445 _JsonUtf8EncoderSink(this._sink, this._toEncodable, this._indent, | 442 _JsonUtf8EncoderSink( |
446 this._bufferSize); | 443 this._sink, this._toEncodable, this._indent, this._bufferSize); |
447 | 444 |
448 /** Callback called for each slice of result bytes. */ | 445 /** Callback called for each slice of result bytes. */ |
449 void _addChunk(Uint8List chunk, int start, int end) { | 446 void _addChunk(Uint8List chunk, int start, int end) { |
450 _sink.addSlice(chunk, start, end, false); | 447 _sink.addSlice(chunk, start, end, false); |
451 } | 448 } |
452 | 449 |
453 void add(Object object) { | 450 void add(Object object) { |
454 if (_isDone) { | 451 if (_isDone) { |
455 throw new StateError("Only one call to add allowed"); | 452 throw new StateError("Only one call to add allowed"); |
456 } | 453 } |
457 _isDone = true; | 454 _isDone = true; |
458 _JsonUtf8Stringifier.stringify(object, _indent, _toEncodable, | 455 _JsonUtf8Stringifier.stringify( |
459 _bufferSize, | 456 object, _indent, _toEncodable, _bufferSize, _addChunk); |
460 _addChunk); | |
461 _sink.close(); | 457 _sink.close(); |
462 } | 458 } |
463 | 459 |
464 void close() { | 460 void close() { |
465 if (!_isDone) { | 461 if (!_isDone) { |
466 _isDone = true; | 462 _isDone = true; |
467 _sink.close(); | 463 _sink.close(); |
468 } | 464 } |
469 } | 465 } |
470 } | 466 } |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
506 */ | 502 */ |
507 external StringConversionSink startChunkedConversion(Sink<Object> sink); | 503 external StringConversionSink startChunkedConversion(Sink<Object> sink); |
508 | 504 |
509 // Override the base class's bind, to provide a better type. | 505 // Override the base class's bind, to provide a better type. |
510 Stream<Object> bind(Stream<String> stream) => super.bind(stream); | 506 Stream<Object> bind(Stream<String> stream) => super.bind(stream); |
511 } | 507 } |
512 | 508 |
513 // Internal optimized JSON parsing implementation. | 509 // Internal optimized JSON parsing implementation. |
514 external _parseJson(String source, reviver(key, value)); | 510 external _parseJson(String source, reviver(key, value)); |
515 | 511 |
516 | |
517 // Implementation of encoder/stringifier. | 512 // Implementation of encoder/stringifier. |
518 | 513 |
519 dynamic _defaultToEncodable(dynamic object) => object.toJson(); | 514 dynamic _defaultToEncodable(dynamic object) => object.toJson(); |
520 | 515 |
521 /** | 516 /** |
522 * JSON encoder that traverses an object structure and writes JSON source. | 517 * JSON encoder that traverses an object structure and writes JSON source. |
523 * | 518 * |
524 * This is an abstract implementation that doesn't decide on the output | 519 * This is an abstract implementation that doesn't decide on the output |
525 * format, but writes the JSON through abstract methods like [writeString]. | 520 * format, but writes the JSON through abstract methods like [writeString]. |
526 */ | 521 */ |
527 abstract class _JsonStringifier { | 522 abstract class _JsonStringifier { |
528 // Character code constants. | 523 // Character code constants. |
529 static const int BACKSPACE = 0x08; | 524 static const int BACKSPACE = 0x08; |
530 static const int TAB = 0x09; | 525 static const int TAB = 0x09; |
531 static const int NEWLINE = 0x0a; | 526 static const int NEWLINE = 0x0a; |
532 static const int CARRIAGE_RETURN = 0x0d; | 527 static const int CARRIAGE_RETURN = 0x0d; |
533 static const int FORM_FEED = 0x0c; | 528 static const int FORM_FEED = 0x0c; |
534 static const int QUOTE = 0x22; | 529 static const int QUOTE = 0x22; |
535 static const int CHAR_0 = 0x30; | 530 static const int CHAR_0 = 0x30; |
536 static const int BACKSLASH = 0x5c; | 531 static const int BACKSLASH = 0x5c; |
537 static const int CHAR_b = 0x62; | 532 static const int CHAR_b = 0x62; |
538 static const int CHAR_f = 0x66; | 533 static const int CHAR_f = 0x66; |
539 static const int CHAR_n = 0x6e; | 534 static const int CHAR_n = 0x6e; |
540 static const int CHAR_r = 0x72; | 535 static const int CHAR_r = 0x72; |
541 static const int CHAR_t = 0x74; | 536 static const int CHAR_t = 0x74; |
542 static const int CHAR_u = 0x75; | 537 static const int CHAR_u = 0x75; |
543 | 538 |
544 /** List of objects currently being traversed. Used to detect cycles. */ | 539 /** List of objects currently being traversed. Used to detect cycles. */ |
545 final List _seen = new List(); | 540 final List _seen = new List(); |
546 /** Function called for each un-encodable object encountered. */ | 541 /** Function called for each un-encodable object encountered. */ |
547 final _ToEncodable _toEncodable; | 542 final _ToEncodable _toEncodable; |
548 | 543 |
549 _JsonStringifier(toEncodable(o)) | 544 _JsonStringifier(toEncodable(o)) |
550 : _toEncodable = toEncodable ?? _defaultToEncodable; | 545 : _toEncodable = toEncodable ?? _defaultToEncodable; |
551 | 546 |
552 /** Append a string to the JSON output. */ | 547 /** Append a string to the JSON output. */ |
(...skipping 15 matching lines...) Expand all Loading... |
568 int offset = 0; | 563 int offset = 0; |
569 final int length = s.length; | 564 final int length = s.length; |
570 for (int i = 0; i < length; i++) { | 565 for (int i = 0; i < length; i++) { |
571 int charCode = s.codeUnitAt(i); | 566 int charCode = s.codeUnitAt(i); |
572 if (charCode > BACKSLASH) continue; | 567 if (charCode > BACKSLASH) continue; |
573 if (charCode < 32) { | 568 if (charCode < 32) { |
574 if (i > offset) writeStringSlice(s, offset, i); | 569 if (i > offset) writeStringSlice(s, offset, i); |
575 offset = i + 1; | 570 offset = i + 1; |
576 writeCharCode(BACKSLASH); | 571 writeCharCode(BACKSLASH); |
577 switch (charCode) { | 572 switch (charCode) { |
578 case BACKSPACE: | 573 case BACKSPACE: |
579 writeCharCode(CHAR_b); | 574 writeCharCode(CHAR_b); |
580 break; | 575 break; |
581 case TAB: | 576 case TAB: |
582 writeCharCode(CHAR_t); | 577 writeCharCode(CHAR_t); |
583 break; | 578 break; |
584 case NEWLINE: | 579 case NEWLINE: |
585 writeCharCode(CHAR_n); | 580 writeCharCode(CHAR_n); |
586 break; | 581 break; |
587 case FORM_FEED: | 582 case FORM_FEED: |
588 writeCharCode(CHAR_f); | 583 writeCharCode(CHAR_f); |
589 break; | 584 break; |
590 case CARRIAGE_RETURN: | 585 case CARRIAGE_RETURN: |
591 writeCharCode(CHAR_r); | 586 writeCharCode(CHAR_r); |
592 break; | 587 break; |
593 default: | 588 default: |
594 writeCharCode(CHAR_u); | 589 writeCharCode(CHAR_u); |
595 writeCharCode(CHAR_0); | 590 writeCharCode(CHAR_0); |
596 writeCharCode(CHAR_0); | 591 writeCharCode(CHAR_0); |
597 writeCharCode(hexDigit((charCode >> 4) & 0xf)); | 592 writeCharCode(hexDigit((charCode >> 4) & 0xf)); |
598 writeCharCode(hexDigit(charCode & 0xf)); | 593 writeCharCode(hexDigit(charCode & 0xf)); |
599 break; | 594 break; |
600 } | 595 } |
601 } else if (charCode == QUOTE || charCode == BACKSLASH) { | 596 } else if (charCode == QUOTE || charCode == BACKSLASH) { |
602 if (i > offset) writeStringSlice(s, offset, i); | 597 if (i > offset) writeStringSlice(s, offset, i); |
603 offset = i + 1; | 598 offset = i + 1; |
604 writeCharCode(BACKSLASH); | 599 writeCharCode(BACKSLASH); |
605 writeCharCode(charCode); | 600 writeCharCode(charCode); |
606 } | 601 } |
607 } | 602 } |
608 if (offset == 0) { | 603 if (offset == 0) { |
609 writeString(s); | 604 writeString(s); |
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
671 bool writeJsonValue(object) { | 666 bool writeJsonValue(object) { |
672 if (object is num) { | 667 if (object is num) { |
673 if (!object.isFinite) return false; | 668 if (!object.isFinite) return false; |
674 writeNumber(object); | 669 writeNumber(object); |
675 return true; | 670 return true; |
676 } else if (identical(object, true)) { | 671 } else if (identical(object, true)) { |
677 writeString('true'); | 672 writeString('true'); |
678 return true; | 673 return true; |
679 } else if (identical(object, false)) { | 674 } else if (identical(object, false)) { |
680 writeString('false'); | 675 writeString('false'); |
681 return true; | 676 return true; |
682 } else if (object == null) { | 677 } else if (object == null) { |
683 writeString('null'); | 678 writeString('null'); |
684 return true; | 679 return true; |
685 } else if (object is String) { | 680 } else if (object is String) { |
686 writeString('"'); | 681 writeString('"'); |
687 writeStringContent(object); | 682 writeStringContent(object); |
688 writeString('"'); | 683 writeString('"'); |
689 return true; | 684 return true; |
690 } else if (object is List) { | 685 } else if (object is List) { |
691 _checkCycle(object); | 686 _checkCycle(object); |
(...skipping 163 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
855 } else { | 850 } else { |
856 stringifier = | 851 stringifier = |
857 new _JsonStringStringifierPretty(output, toEncodable, indent); | 852 new _JsonStringStringifierPretty(output, toEncodable, indent); |
858 } | 853 } |
859 stringifier.writeObject(object); | 854 stringifier.writeObject(object); |
860 } | 855 } |
861 | 856 |
862 void writeNumber(num number) { | 857 void writeNumber(num number) { |
863 _sink.write(number.toString()); | 858 _sink.write(number.toString()); |
864 } | 859 } |
| 860 |
865 void writeString(String string) { | 861 void writeString(String string) { |
866 _sink.write(string); | 862 _sink.write(string); |
867 } | 863 } |
| 864 |
868 void writeStringSlice(String string, int start, int end) { | 865 void writeStringSlice(String string, int start, int end) { |
869 _sink.write(string.substring(start, end)); | 866 _sink.write(string.substring(start, end)); |
870 } | 867 } |
| 868 |
871 void writeCharCode(int charCode) { | 869 void writeCharCode(int charCode) { |
872 _sink.writeCharCode(charCode); | 870 _sink.writeCharCode(charCode); |
873 } | 871 } |
874 } | 872 } |
875 | 873 |
876 class _JsonStringStringifierPretty extends _JsonStringStringifier | 874 class _JsonStringStringifierPretty extends _JsonStringStringifier |
877 with _JsonPrettyPrintMixin { | 875 with _JsonPrettyPrintMixin { |
878 final String _indent; | 876 final String _indent; |
879 | 877 |
880 _JsonStringStringifierPretty(StringSink sink, toEncodable(o), this._indent) | 878 _JsonStringStringifierPretty(StringSink sink, toEncodable(o), this._indent) |
881 : super(sink, toEncodable); | 879 : super(sink, toEncodable); |
882 | 880 |
883 void writeIndentation(int count) { | 881 void writeIndentation(int count) { |
884 for (int i = 0; i < count; i++) writeString(_indent); | 882 for (int i = 0; i < count; i++) writeString(_indent); |
885 } | 883 } |
886 } | 884 } |
887 | 885 |
(...skipping 20 matching lines...) Expand all Loading... |
908 * Convert [object] to UTF-8 encoded JSON. | 906 * Convert [object] to UTF-8 encoded JSON. |
909 * | 907 * |
910 * Calls [addChunk] with slices of UTF-8 code units. | 908 * Calls [addChunk] with slices of UTF-8 code units. |
911 * These will typically have size [bufferSize], but may be shorter. | 909 * These will typically have size [bufferSize], but may be shorter. |
912 * The buffers are not reused, so the [addChunk] call may keep and reuse the | 910 * The buffers are not reused, so the [addChunk] call may keep and reuse the |
913 * chunks. | 911 * chunks. |
914 * | 912 * |
915 * If [indent] is non-`null`, the result will be "pretty-printed" with extra | 913 * If [indent] is non-`null`, the result will be "pretty-printed" with extra |
916 * newlines and indentation, using [indent] as the indentation. | 914 * newlines and indentation, using [indent] as the indentation. |
917 */ | 915 */ |
918 static void stringify(Object object, | 916 static void stringify(Object object, List<int> indent, toEncodable(o), |
919 List<int> indent, | 917 int bufferSize, void addChunk(Uint8List chunk, int start, int end)) { |
920 toEncodable(o), | |
921 int bufferSize, | |
922 void addChunk(Uint8List chunk, int start, int end)) { | |
923 _JsonUtf8Stringifier stringifier; | 918 _JsonUtf8Stringifier stringifier; |
924 if (indent != null) { | 919 if (indent != null) { |
925 stringifier = new _JsonUtf8StringifierPretty(toEncodable, indent, | 920 stringifier = new _JsonUtf8StringifierPretty( |
926 bufferSize, addChunk); | 921 toEncodable, indent, bufferSize, addChunk); |
927 } else { | 922 } else { |
928 stringifier = new _JsonUtf8Stringifier(toEncodable, bufferSize, addChunk); | 923 stringifier = new _JsonUtf8Stringifier(toEncodable, bufferSize, addChunk); |
929 } | 924 } |
930 stringifier.writeObject(object); | 925 stringifier.writeObject(object); |
931 stringifier.flush(); | 926 stringifier.flush(); |
932 } | 927 } |
933 | 928 |
934 /** | 929 /** |
935 * Must be called at the end to push the last chunk to the [addChunk] | 930 * Must be called at the end to push the last chunk to the [addChunk] |
936 * callback. | 931 * callback. |
(...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1026 index = 0; | 1021 index = 0; |
1027 } | 1022 } |
1028 buffer[index++] = byte; | 1023 buffer[index++] = byte; |
1029 } | 1024 } |
1030 } | 1025 } |
1031 | 1026 |
1032 /** | 1027 /** |
1033 * Pretty-printing version of [_JsonUtf8Stringifier]. | 1028 * Pretty-printing version of [_JsonUtf8Stringifier]. |
1034 */ | 1029 */ |
1035 class _JsonUtf8StringifierPretty extends _JsonUtf8Stringifier | 1030 class _JsonUtf8StringifierPretty extends _JsonUtf8Stringifier |
1036 with _JsonPrettyPrintMixin { | 1031 with _JsonPrettyPrintMixin { |
1037 final List<int> indent; | 1032 final List<int> indent; |
1038 _JsonUtf8StringifierPretty( | 1033 _JsonUtf8StringifierPretty(toEncodable(o), this.indent, bufferSize, |
1039 toEncodable(o), this.indent, | 1034 void addChunk(Uint8List buffer, int start, int end)) |
1040 bufferSize, void addChunk(Uint8List buffer, int start, int end)) | |
1041 : super(toEncodable, bufferSize, addChunk); | 1035 : super(toEncodable, bufferSize, addChunk); |
1042 | 1036 |
1043 void writeIndentation(int count) { | 1037 void writeIndentation(int count) { |
1044 List<int> indent = this.indent; | 1038 List<int> indent = this.indent; |
1045 int indentLength = indent.length; | 1039 int indentLength = indent.length; |
1046 if (indentLength == 1) { | 1040 if (indentLength == 1) { |
1047 int char = indent[0]; | 1041 int char = indent[0]; |
1048 while (count > 0) { | 1042 while (count > 0) { |
1049 writeByte(char); | 1043 writeByte(char); |
1050 count -= 1; | 1044 count -= 1; |
1051 } | 1045 } |
1052 return; | 1046 return; |
1053 } | 1047 } |
1054 while (count > 0) { | 1048 while (count > 0) { |
1055 count--; | 1049 count--; |
1056 int end = index + indentLength; | 1050 int end = index + indentLength; |
1057 if (end <= buffer.length) { | 1051 if (end <= buffer.length) { |
1058 buffer.setRange(index, end, indent); | 1052 buffer.setRange(index, end, indent); |
1059 index = end; | 1053 index = end; |
1060 } else { | 1054 } else { |
1061 for (int i = 0; i < indentLength; i++) { | 1055 for (int i = 0; i < indentLength; i++) { |
1062 writeByte(indent[i]); | 1056 writeByte(indent[i]); |
1063 } | 1057 } |
1064 } | 1058 } |
1065 } | 1059 } |
1066 } | 1060 } |
1067 } | 1061 } |
OLD | NEW |