| 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 library dart.json; | 5 library dart.json; |
| 6 | 6 |
| 7 // JSON parsing and serialization. | 7 // JSON parsing and serialization. |
| 8 | 8 |
| 9 /** | 9 /** |
| 10 * Error thrown by JSON serialization if an object cannot be serialized. | 10 * Error thrown by JSON serialization if an object cannot be serialized. |
| 11 * | 11 * |
| 12 * The [unsupportedObject] field holds that object that failed to be serialized. | 12 * The [unsupportedObject] field holds that object that failed to be serialized. |
| 13 * | 13 * |
| 14 * If an object isn't directly serializable, the serializer calls the 'toJson' | 14 * If an object isn't directly serializable, the serializer calls the 'toJson' |
| 15 * method on the object. If that call fails, the error will be stored in the | 15 * method on the object. If that call fails, the error will be stored in the |
| 16 * [cause] field. If the call returns an object that isn't directly | 16 * [cause] field. If the call returns an object that isn't directly |
| 17 * serializable, the [cause] will be null. | 17 * serializable, the [cause] will be null. |
| 18 */ | 18 */ |
| 19 class JsonUnsupportedObjectError implements Error { | 19 class JsonUnsupportedObjectError implements Error { |
| 20 /** The object that could not be serialized. */ | 20 /** The object that could not be serialized. */ |
| 21 final unsupportedObject; | 21 final unsupportedObject; |
| 22 /** The exception thrown by object's [:toJson:] method, if any. */ | 22 /** The exception thrown by object's [:toJson:] method, if any. */ |
| 23 final cause; | 23 final cause; |
| 24 JsonUnsupportedObjectError(this.unsupportedObject) : cause = null; | 24 |
| 25 JsonUnsupportedObjectError.withCause(this.unsupportedObject, this.cause); | 25 JsonUnsupportedObjectError(this.unsupportedObject, { this.cause }); |
| 26 | 26 |
| 27 String toString() { | 27 String toString() { |
| 28 if (cause != null) { | 28 if (cause != null) { |
| 29 return "Calling toJson method on object failed."; | 29 return "Calling toJson method on object failed."; |
| 30 } else { | 30 } else { |
| 31 return "Object toJson method returns non-serializable value."; | 31 return "Object toJson method returns non-serializable value."; |
| 32 } | 32 } |
| 33 } | 33 } |
| 34 } | 34 } |
| 35 | 35 |
| 36 | 36 |
| 37 /** | 37 /** |
| 38 * Reports that an object could not be stringified due to cyclic references. |
| 39 * |
| 40 * An object that references itself cannot be serialized by [stringify]. |
| 41 * When the cycle is detected, a [JsonCyclicError] is thrown. |
| 42 */ |
| 43 class JsonCyclicError extends JsonUnsupportedObjectError { |
| 44 /** The first object that was detected as part of a cycle. */ |
| 45 JsonCyclicError(Object object): super(object); |
| 46 String toString() => "Cyclic error in JSON stringify"; |
| 47 } |
| 48 |
| 49 |
| 50 /** |
| 38 * Parses [json] and build the corresponding parsed JSON value. | 51 * Parses [json] and build the corresponding parsed JSON value. |
| 39 * | 52 * |
| 40 * Parsed JSON values are of the types [num], [String], [bool], [Null], | 53 * Parsed JSON values are of the types [num], [String], [bool], [Null], |
| 41 * [List]s of parsed JSON values or [Map]s from [String] to parsed | 54 * [List]s of parsed JSON values or [Map]s from [String] to parsed |
| 42 * JSON values. | 55 * JSON values. |
| 43 * | 56 * |
| 44 * The optional [revivier] function, if provided, is called once for each | 57 * The optional [revivier] function, if provided, is called once for each |
| 45 * object or list property parsed. The arguments are the property name | 58 * object or list property parsed. The arguments are the property name |
| 46 * ([String]) or list index ([int]), and the value is the parsed value. | 59 * ([String]) or list index ([int]), and the value is the parsed value. |
| 47 * The return value of the revivier will be used as the value of that property | 60 * The return value of the revivier will be used as the value of that property |
| (...skipping 10 matching lines...) Expand all Loading... |
| 58 } else { | 71 } else { |
| 59 listener = new ReviverJsonListener(reviver); | 72 listener = new ReviverJsonListener(reviver); |
| 60 } | 73 } |
| 61 new JsonParser(json, listener).parse(); | 74 new JsonParser(json, listener).parse(); |
| 62 return listener.result; | 75 return listener.result; |
| 63 } | 76 } |
| 64 | 77 |
| 65 /** | 78 /** |
| 66 * Serializes [object] into a JSON string. | 79 * Serializes [object] into a JSON string. |
| 67 * | 80 * |
| 68 * Directly serializable types are [num], [String], [bool], [Null], [List] | 81 * Directly serializable values are [num], [String], [bool], and [Null], as well |
| 69 * and [Map]. | 82 * as some [List] and [Map] values. |
| 70 * For [List], the elements must all be serializable. | 83 * For [List], the elements must all be serializable. |
| 71 * For [Map], the keys must be [String] and the values must be serializable. | 84 * For [Map], the keys must be [String] and the values must be serializable. |
| 85 * |
| 72 * If a value is any other type is attempted serialized, a "toJson()" method | 86 * If a value is any other type is attempted serialized, a "toJson()" method |
| 73 * is invoked on the object and the result, which must be a directly | 87 * is invoked on the object and the result, which must be a directly |
| 74 * serializable type, is serialized instead of the original value. | 88 * serializable value, is serialized instead of the original value. |
| 89 * |
| 75 * If the object does not support this method, throws, or returns a | 90 * If the object does not support this method, throws, or returns a |
| 76 * value that is not directly serializable, a [JsonUnsupportedObjectError] | 91 * value that is not directly serializable, a [JsonUnsupportedObjectError] |
| 77 * exception is thrown. If the call throws (including the case where there | 92 * exception is thrown. If the call throws (including the case where there |
| 78 * is no nullary "toJson" method, the error is caught and stored in the | 93 * is no nullary "toJson" method, the error is caught and stored in the |
| 79 * [JsonUnsupportedObjectError]'s [:cause:] field. | 94 * [JsonUnsupportedObjectError]'s [:cause:] field. |
| 80 *Json | 95 * |
| 81 * Objects should not change during serialization. | 96 * If a [List] or [Map] contains a reference to itself, directly or through |
| 97 * other lists or maps, it cannot be serialized and a [JsonCyclicError] is |
| 98 * thrown. |
| 99 * |
| 100 * Json Objects should not change during serialization. |
| 82 * If an object is serialized more than once, [stringify] is allowed to cache | 101 * If an object is serialized more than once, [stringify] is allowed to cache |
| 83 * the JSON text for it. I.e., if an object changes after it is first | 102 * the JSON text for it. I.e., if an object changes after it is first |
| 84 * serialized, the new values may or may not be reflected in the result. | 103 * serialized, the new values may or may not be reflected in the result. |
| 85 */ | 104 */ |
| 86 String stringify(Object object) { | 105 String stringify(Object object) { |
| 87 return _JsonStringifier.stringify(object); | 106 return _JsonStringifier.stringify(object); |
| 88 } | 107 } |
| 89 | 108 |
| 90 /** | 109 /** |
| 91 * Serializes [object] into [output] stream. | 110 * Serializes [object] into [output] stream. |
| (...skipping 618 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 710 charCodes.add(charCode); | 729 charCodes.add(charCode); |
| 711 } | 730 } |
| 712 } | 731 } |
| 713 sb.write(needsEscape ? new String.fromCharCodes(charCodes) : s); | 732 sb.write(needsEscape ? new String.fromCharCodes(charCodes) : s); |
| 714 } | 733 } |
| 715 | 734 |
| 716 void checkCycle(final object) { | 735 void checkCycle(final object) { |
| 717 // TODO: use Iterables. | 736 // TODO: use Iterables. |
| 718 for (int i = 0; i < seen.length; i++) { | 737 for (int i = 0; i < seen.length; i++) { |
| 719 if (identical(seen[i], object)) { | 738 if (identical(seen[i], object)) { |
| 720 throw 'Cyclic structure'; | 739 throw new JsonCyclicError(object); |
| 721 } | 740 } |
| 722 } | 741 } |
| 723 seen.add(object); | 742 seen.add(object); |
| 724 } | 743 } |
| 725 | 744 |
| 726 void stringifyValue(final object) { | 745 void stringifyValue(final object) { |
| 727 // Tries stringifying object directly. If it's not a simple value, List or | 746 // Tries stringifying object directly. If it's not a simple value, List or |
| 728 // Map, call toJson() to get a custom representation and try serializing | 747 // Map, call toJson() to get a custom representation and try serializing |
| 729 // that. | 748 // that. |
| 730 if (!stringifyJsonValue(object)) { | 749 if (!stringifyJsonValue(object)) { |
| 731 checkCycle(object); | 750 checkCycle(object); |
| 732 try { | 751 try { |
| 733 var customJson = object.toJson(); | 752 var customJson = object.toJson(); |
| 734 if (!stringifyJsonValue(customJson)) { | 753 if (!stringifyJsonValue(customJson)) { |
| 735 throw new JsonUnsupportedObjectError(object); | 754 throw new JsonUnsupportedObjectError(object); |
| 736 } | 755 } |
| 737 seen.removeLast(); | 756 seen.removeLast(); |
| 738 } catch (e) { | 757 } catch (e) { |
| 739 throw new JsonUnsupportedObjectError.withCause(object, e); | 758 throw new JsonUnsupportedObjectError(object, cause: e); |
| 740 } | 759 } |
| 741 } | 760 } |
| 742 } | 761 } |
| 743 | 762 |
| 744 /** | 763 /** |
| 745 * Serializes a [num], [String], [bool], [Null], [List] or [Map] value. | 764 * Serializes a [num], [String], [bool], [Null], [List] or [Map] value. |
| 746 * | 765 * |
| 747 * Returns true if the value is one of these types, and false if not. | 766 * Returns true if the value is one of these types, and false if not. |
| 748 * If a value is both a [List] and a [Map], it's serialized as a [List]. | 767 * If a value is both a [List] and a [Map], it's serialized as a [List]. |
| 749 */ | 768 */ |
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 798 first = false; | 817 first = false; |
| 799 }); | 818 }); |
| 800 sb.write('}'); | 819 sb.write('}'); |
| 801 seen.removeLast(); | 820 seen.removeLast(); |
| 802 return true; | 821 return true; |
| 803 } else { | 822 } else { |
| 804 return false; | 823 return false; |
| 805 } | 824 } |
| 806 } | 825 } |
| 807 } | 826 } |
| OLD | NEW |