Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(258)

Side by Side Diff: sdk/lib/json/json.dart

Issue 14289002: Make JSON stringify throw a JsonUnsupportedObjectError on cycles. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | tests/lib/json/json_test.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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 }
OLDNEW
« no previous file with comments | « no previous file | tests/lib/json/json_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698