| OLD | NEW |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 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 /** | 5 /** |
| 6 * Utilities for encoding and decoding JSON (JavaScript Object Notation) data. | 6 * Utilities for encoding and decoding JSON (JavaScript Object Notation) data. |
| 7 */ | 7 */ |
| 8 | 8 |
| 9 @deprecated | 9 @deprecated |
| 10 library dart.json; | 10 library dart.json; |
| 11 | 11 |
| 12 import "dart:convert"; | 12 import "dart:convert"; |
| 13 import "dart:_collection-dev" show deprecated; | 13 import "dart:_collection-dev" show deprecated; |
| 14 import "dart:collection" show HashSet; |
| 14 export "dart:convert" show JsonUnsupportedObjectError, JsonCyclicError; | 15 export "dart:convert" show JsonUnsupportedObjectError, JsonCyclicError; |
| 15 | 16 |
| 16 // JSON parsing and serialization. | 17 // JSON parsing and serialization. |
| 17 | 18 |
| 18 /** | 19 /** |
| 19 * *DEPRECATED* Use `dart:convert JSON.decode` instead. | 20 * *DEPRECATED* Use `dart:convert JSON.decode` instead. |
| 20 * | 21 * |
| 21 * Parses [json] and build the corresponding parsed JSON value. | 22 * Parses [json] and build the corresponding parsed JSON value. |
| 22 * | 23 * |
| 23 * Parsed JSON values are of the types [num], [String], [bool], [Null], | 24 * Parsed JSON values are of the types [num], [String], [bool], [Null], |
| (...skipping 20 matching lines...) Expand all Loading... |
| 44 /** | 45 /** |
| 45 * *DEPRECATED* Use `dart:convert JSON.encode` instead. | 46 * *DEPRECATED* Use `dart:convert JSON.encode` instead. |
| 46 * | 47 * |
| 47 * Serializes [object] into a JSON string. | 48 * Serializes [object] into a JSON string. |
| 48 * | 49 * |
| 49 * Directly serializable values are [num], [String], [bool], and [Null], as well | 50 * Directly serializable values are [num], [String], [bool], and [Null], as well |
| 50 * as some [List] and [Map] values. | 51 * as some [List] and [Map] values. |
| 51 * For [List], the elements must all be serializable. | 52 * For [List], the elements must all be serializable. |
| 52 * For [Map], the keys must be [String] and the values must be serializable. | 53 * For [Map], the keys must be [String] and the values must be serializable. |
| 53 * | 54 * |
| 54 * If a value is any other type is attempted serialized, a "toJson()" method | 55 * If a value is any other type is attempted serialized, the [toEncodable] |
| 55 * is invoked on the object and the result, which must be a directly | 56 * method is called with the object as argument, and the result, which must be a |
| 56 * serializable value, is serialized instead of the original value. | 57 * directly serializable value, is serialized instead of the original value. |
| 58 * If [toEncodable] is omitted, the default is to call `object.toJson()` on the |
| 59 * object. |
| 57 * | 60 * |
| 58 * If the object does not support this method, throws, or returns a | 61 * If the conversion throws, or returns a value that is not directly |
| 59 * value that is not directly serializable, a [JsonUnsupportedObjectError] | 62 * serializable, a [JsonUnsupportedObjectError] exception is thrown. |
| 60 * exception is thrown. If the call throws (including the case where there | 63 * If the call throws (including the case where there |
| 61 * is no nullary "toJson" method, the error is caught and stored in the | 64 * is no nullary "toJson" method), the error is caught and stored in the |
| 62 * [JsonUnsupportedObjectError]'s [:cause:] field. | 65 * [JsonUnsupportedObjectError]'s [:cause:] field. |
| 63 * | 66 * |
| 64 * If a [List] or [Map] contains a reference to itself, directly or through | 67 * If a [List] or [Map] contains a reference to itself, directly or through |
| 65 * other lists or maps, it cannot be serialized and a [JsonCyclicError] is | 68 * other lists or maps, it cannot be serialized and a [JsonCyclicError] is |
| 66 * thrown. | 69 * thrown. |
| 67 * | 70 * |
| 68 * Json Objects should not change during serialization. | 71 * The objects being serialized should not change during serialization. |
| 69 * If an object is serialized more than once, [stringify] is allowed to cache | 72 * If an object is serialized more than once, [stringify] is allowed to cache |
| 70 * the JSON text for it. I.e., if an object changes after it is first | 73 * the JSON text for it. I.e., if an object changes after it is first |
| 71 * serialized, the new values may or may not be reflected in the result. | 74 * serialized, the new values may or may not be reflected in the result. |
| 72 */ | 75 */ |
| 73 @deprecated | 76 @deprecated |
| 74 String stringify(Object object) { | 77 String stringify(Object object, [toEncodable(object)]) { |
| 75 return _JsonStringifier.stringify(object); | 78 if (toEncodable == null) toEncodable = _defaultToEncodable; |
| 79 return _JsonStringifier.stringify(object, toEncodable); |
| 76 } | 80 } |
| 77 | 81 |
| 78 /** | 82 /** |
| 79 * *DEPRECATED* Use `package:json/json.dart` or `dart:convert` instead. | 83 * *DEPRECATED* Use `package:json/json.dart` or `dart:convert` instead. |
| 80 * | 84 * |
| 81 * Serializes [object] into [output] stream. | 85 * Serializes [object] into [output] stream. |
| 82 * | 86 * |
| 83 * Performs the same operations as [stringify] but outputs the resulting | 87 * Performs the same operations as [stringify] but outputs the resulting |
| 84 * string to an existing [StringSink] instead of creating a new [String]. | 88 * string to an existing [StringSink] instead of creating a new [String]. |
| 85 * | 89 * |
| 86 * If serialization fails by throwing, some data might have been added to | 90 * If serialization fails by throwing, some data might have been added to |
| 87 * [output], but it won't contain valid JSON text. | 91 * [output], but it won't contain valid JSON text. |
| 88 */ | 92 */ |
| 89 @deprecated | 93 @deprecated |
| 90 void printOn(Object object, StringSink output) { | 94 void printOn(Object object, StringSink output, [ toEncodable(object) ]) { |
| 91 return _JsonStringifier.printOn(object, output); | 95 if (toEncodable == null) toEncodable = _defaultToEncodable; |
| 96 return _JsonStringifier.printOn(object, output, toEncodable); |
| 92 } | 97 } |
| 93 | 98 |
| 94 //// Implementation /////////////////////////////////////////////////////////// | 99 //// Implementation /////////////////////////////////////////////////////////// |
| 95 | 100 |
| 96 // Simple API for JSON parsing. | 101 // Simple API for JSON parsing. |
| 97 | 102 |
| 98 /// *DEPRECATED* Use `package:json/json.dart` instead. | 103 /// *DEPRECATED* Use `package:json/json.dart` instead. |
| 99 @deprecated | 104 @deprecated |
| 100 abstract class JsonListener { | 105 abstract class JsonListener { |
| 101 void handleString(String value) {} | 106 void handleString(String value) {} |
| (...skipping 534 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 636 int sliceEnd = position + 20; | 641 int sliceEnd = position + 20; |
| 637 if (sliceEnd > source.length) { | 642 if (sliceEnd > source.length) { |
| 638 slice = "'${source.substring(position)}'"; | 643 slice = "'${source.substring(position)}'"; |
| 639 } else { | 644 } else { |
| 640 slice = "'${source.substring(position, sliceEnd)}...'"; | 645 slice = "'${source.substring(position, sliceEnd)}...'"; |
| 641 } | 646 } |
| 642 throw new FormatException("Unexpected character at $position: $slice"); | 647 throw new FormatException("Unexpected character at $position: $slice"); |
| 643 } | 648 } |
| 644 } | 649 } |
| 645 | 650 |
| 651 Object _defaultToEncodable(object) => object.toJson(); |
| 646 | 652 |
| 647 class _JsonStringifier { | 653 class _JsonStringifier { |
| 648 StringSink sink; | 654 final Function toEncodable; |
| 649 List<Object> seen; // TODO: that should be identity set. | 655 final StringSink sink; |
| 656 final Set<Object> seen; |
| 650 | 657 |
| 651 _JsonStringifier(this.sink) : seen = []; | 658 _JsonStringifier(this.sink, this.toEncodable) |
| 659 : this.seen = new HashSet.identity(); |
| 652 | 660 |
| 653 static String stringify(final object) { | 661 static String stringify(final object, toEncodable(object)) { |
| 654 StringBuffer output = new StringBuffer(); | 662 StringBuffer output = new StringBuffer(); |
| 655 _JsonStringifier stringifier = new _JsonStringifier(output); | 663 _JsonStringifier stringifier = new _JsonStringifier(output, toEncodable); |
| 656 stringifier.stringifyValue(object); | 664 stringifier.stringifyValue(object); |
| 657 return output.toString(); | 665 return output.toString(); |
| 658 } | 666 } |
| 659 | 667 |
| 660 static void printOn(final object, StringSink output) { | 668 static void printOn(final object, StringSink output, toEncodable(object)) { |
| 661 _JsonStringifier stringifier = new _JsonStringifier(output); | 669 _JsonStringifier stringifier = new _JsonStringifier(output, toEncodable); |
| 662 stringifier.stringifyValue(object); | 670 stringifier.stringifyValue(object); |
| 663 } | 671 } |
| 664 | 672 |
| 665 static String numberToString(num x) { | 673 static String numberToString(num x) { |
| 666 return x.toString(); | 674 return x.toString(); |
| 667 } | 675 } |
| 668 | 676 |
| 669 // ('0' + x) or ('a' + x - 10) | 677 // ('0' + x) or ('a' + x - 10) |
| 670 static int hexDigit(int x) => x < 10 ? 48 + x : 87 + x; | 678 static int hexDigit(int x) => x < 10 ? 48 + x : 87 + x; |
| 671 | 679 |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 708 charCodes.add(JsonParser.BACKSLASH); | 716 charCodes.add(JsonParser.BACKSLASH); |
| 709 charCodes.add(charCode); | 717 charCodes.add(charCode); |
| 710 } else { | 718 } else { |
| 711 charCodes.add(charCode); | 719 charCodes.add(charCode); |
| 712 } | 720 } |
| 713 } | 721 } |
| 714 sb.write(needsEscape ? new String.fromCharCodes(charCodes) : s); | 722 sb.write(needsEscape ? new String.fromCharCodes(charCodes) : s); |
| 715 } | 723 } |
| 716 | 724 |
| 717 void checkCycle(final object) { | 725 void checkCycle(final object) { |
| 718 // TODO: use Iterables. | 726 if (seen.contains(object)) { |
| 719 for (int i = 0; i < seen.length; i++) { | 727 throw new JsonCyclicError(object); |
| 720 if (identical(seen[i], object)) { | |
| 721 throw new JsonCyclicError(object); | |
| 722 } | |
| 723 } | 728 } |
| 724 seen.add(object); | 729 seen.add(object); |
| 725 } | 730 } |
| 726 | 731 |
| 727 void stringifyValue(final object) { | 732 void stringifyValue(final object) { |
| 728 // Tries stringifying object directly. If it's not a simple value, List or | 733 // Tries stringifying object directly. If it's not a simple value, List or |
| 729 // Map, call toJson() to get a custom representation and try serializing | 734 // Map, call toJson() to get a custom representation and try serializing |
| 730 // that. | 735 // that. |
| 731 if (!stringifyJsonValue(object)) { | 736 if (!stringifyJsonValue(object)) { |
| 732 checkCycle(object); | 737 checkCycle(object); |
| 733 try { | 738 try { |
| 734 var customJson = object.toJson(); | 739 var customJson = toEncodable(object); |
| 735 if (!stringifyJsonValue(customJson)) { | 740 if (!stringifyJsonValue(customJson)) { |
| 736 throw new JsonUnsupportedObjectError(object); | 741 throw new JsonUnsupportedObjectError(object); |
| 737 } | 742 } |
| 738 seen.removeLast(); | 743 seen.remove(object); |
| 739 } catch (e) { | 744 } catch (e) { |
| 740 throw new JsonUnsupportedObjectError(object, cause: e); | 745 throw new JsonUnsupportedObjectError(object, cause: e); |
| 741 } | 746 } |
| 742 } | 747 } |
| 743 } | 748 } |
| 744 | 749 |
| 745 /** | 750 /** |
| 746 * Serializes a [num], [String], [bool], [Null], [List] or [Map] value. | 751 * Serializes a [num], [String], [bool], [Null], [List] or [Map] value. |
| 747 * | 752 * |
| 748 * Returns true if the value is one of these types, and false if not. | 753 * Returns true if the value is one of these types, and false if not. |
| (...skipping 24 matching lines...) Expand all Loading... |
| 773 sink.write('['); | 778 sink.write('['); |
| 774 if (a.length > 0) { | 779 if (a.length > 0) { |
| 775 stringifyValue(a[0]); | 780 stringifyValue(a[0]); |
| 776 // TODO: switch to Iterables. | 781 // TODO: switch to Iterables. |
| 777 for (int i = 1; i < a.length; i++) { | 782 for (int i = 1; i < a.length; i++) { |
| 778 sink.write(','); | 783 sink.write(','); |
| 779 stringifyValue(a[i]); | 784 stringifyValue(a[i]); |
| 780 } | 785 } |
| 781 } | 786 } |
| 782 sink.write(']'); | 787 sink.write(']'); |
| 783 seen.removeLast(); | 788 seen.remove(object); |
| 784 return true; | 789 return true; |
| 785 } else if (object is Map) { | 790 } else if (object is Map) { |
| 786 checkCycle(object); | 791 checkCycle(object); |
| 787 Map<String, Object> m = object; | 792 Map<String, Object> m = object; |
| 788 sink.write('{'); | 793 sink.write('{'); |
| 789 bool first = true; | 794 bool first = true; |
| 790 m.forEach((String key, Object value) { | 795 m.forEach((String key, Object value) { |
| 791 if (!first) { | 796 if (!first) { |
| 792 sink.write(',"'); | 797 sink.write(',"'); |
| 793 } else { | 798 } else { |
| 794 sink.write('"'); | 799 sink.write('"'); |
| 795 } | 800 } |
| 796 escape(sink, key); | 801 escape(sink, key); |
| 797 sink.write('":'); | 802 sink.write('":'); |
| 798 stringifyValue(value); | 803 stringifyValue(value); |
| 799 first = false; | 804 first = false; |
| 800 }); | 805 }); |
| 801 sink.write('}'); | 806 sink.write('}'); |
| 802 seen.removeLast(); | 807 seen.remove(object); |
| 803 return true; | 808 return true; |
| 804 } else { | 809 } else { |
| 805 return false; | 810 return false; |
| 806 } | 811 } |
| 807 } | 812 } |
| 808 } | 813 } |
| OLD | NEW |