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 |