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

Side by Side Diff: runtime/lib/convert_patch.dart

Issue 23554004: Made old dart:json library use convert to parse JSON. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Addres review comments. Created 7 years, 3 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 | runtime/lib/convert_sources.gypi » ('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) 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 /** 5 // JSON conversion.
6 * Utilities for encoding and decoding JSON (JavaScript Object Notation) data.
7 */
8 6
9 library json; 7 patch _parseJson(String json, reviver(var key, var value)) {
10 8 _BuildJsonListener listener;
11 // JSON parsing and serialization. 9 if (reviver == null) {
12 10 listener = new _BuildJsonListener();
13 /** 11 } else {
14 * Error thrown by JSON serialization if an object cannot be serialized. 12 listener = new _ReviverJsonListener(reviver);
15 *
16 * The [unsupportedObject] field holds that object that failed to be serialized.
17 *
18 * If an object isn't directly serializable, the serializer calls the 'toJson'
19 * method on the object. If that call fails, the error will be stored in the
20 * [cause] field. If the call returns an object that isn't directly
21 * serializable, the [cause] will be null.
22 */
23 class JsonUnsupportedObjectError extends Error {
24 /** The object that could not be serialized. */
25 final unsupportedObject;
26 /** The exception thrown by object's [:toJson:] method, if any. */
27 final cause;
28
29 JsonUnsupportedObjectError(this.unsupportedObject, { this.cause });
30
31 String toString() {
32 if (cause != null) {
33 return "Calling toJson method on object failed.";
34 } else {
35 return "Object toJson method returns non-serializable value.";
36 }
37 } 13 }
38 } 14 new _JsonParser(json, listener).parse();
39
40
41 /**
42 * Reports that an object could not be stringified due to cyclic references.
43 *
44 * An object that references itself cannot be serialized by [stringify].
45 * When the cycle is detected, a [JsonCyclicError] is thrown.
46 */
47 class JsonCyclicError extends JsonUnsupportedObjectError {
48 /** The first object that was detected as part of a cycle. */
49 JsonCyclicError(Object object): super(object);
50 String toString() => "Cyclic error in JSON stringify";
51 }
52
53
54 /**
55 * Parses [json] and build the corresponding parsed JSON value.
56 *
57 * Parsed JSON values are of the types [num], [String], [bool], [Null],
58 * [List]s of parsed JSON values or [Map]s from [String] to parsed
59 * JSON values.
60 *
61 * The optional [reviver] function, if provided, is called once for each
62 * object or list property parsed. The arguments are the property name
63 * ([String]) or list index ([int]), and the value is the parsed value.
64 * The return value of the reviver will be used as the value of that property
65 * instead the parsed value.
66 *
67 * Throws [FormatException] if the input is not valid JSON text.
68 */
69 parse(String json, [reviver(var key, var value)]) {
70 BuildJsonListener listener;
71 if (reviver == null) {
72 listener = new BuildJsonListener();
73 } else {
74 listener = new ReviverJsonListener(reviver);
75 }
76 new JsonParser(json, listener).parse();
77 return listener.result; 15 return listener.result;
78 } 16 }
79 17
80 /**
81 * Serializes [object] into a JSON string.
82 *
83 * Directly serializable values are [num], [String], [bool], and [Null], as well
84 * as some [List] and [Map] values.
85 * For [List], the elements must all be serializable.
86 * For [Map], the keys must be [String] and the values must be serializable.
87 *
88 * If a value is any other type is attempted serialized, a "toJson()" method
89 * is invoked on the object and the result, which must be a directly
90 * serializable value, is serialized instead of the original value.
91 *
92 * If the object does not support this method, throws, or returns a
93 * value that is not directly serializable, a [JsonUnsupportedObjectError]
94 * exception is thrown. If the call throws (including the case where there
95 * is no nullary "toJson" method, the error is caught and stored in the
96 * [JsonUnsupportedObjectError]'s [:cause:] field.
97 *
98 * If a [List] or [Map] contains a reference to itself, directly or through
99 * other lists or maps, it cannot be serialized and a [JsonCyclicError] is
100 * thrown.
101 *
102 * Json Objects should not change during serialization.
103 * If an object is serialized more than once, [stringify] is allowed to cache
104 * the JSON text for it. I.e., if an object changes after it is first
105 * serialized, the new values may or may not be reflected in the result.
106 */
107 String stringify(Object object) {
108 return _JsonStringifier.stringify(object);
109 }
110
111 /**
112 * Serializes [object] into [output] stream.
113 *
114 * Performs the same operations as [stringify] but outputs the resulting
115 * string to an existing [StringSink] instead of creating a new [String].
116 *
117 * If serialization fails by throwing, some data might have been added to
118 * [output], but it won't contain valid JSON text.
119 */
120 void printOn(Object object, StringSink output) {
121 return _JsonStringifier.printOn(object, output);
122 }
123
124 //// Implementation /////////////////////////////////////////////////////////// 18 //// Implementation ///////////////////////////////////////////////////////////
125 19
126 // Simple API for JSON parsing. 20 // Simple API for JSON parsing.
127 21
128 abstract class JsonListener { 22 abstract class _JsonListener {
129 void handleString(String value) {} 23 void handleString(String value) {}
130 void handleNumber(num value) {} 24 void handleNumber(num value) {}
131 void handleBool(bool value) {} 25 void handleBool(bool value) {}
132 void handleNull() {} 26 void handleNull() {}
133 void beginObject() {} 27 void beginObject() {}
134 void propertyName() {} 28 void propertyName() {}
135 void propertyValue() {} 29 void propertyValue() {}
136 void endObject() {} 30 void endObject() {}
137 void beginArray() {} 31 void beginArray() {}
138 void arrayElement() {} 32 void arrayElement() {}
139 void endArray() {} 33 void endArray() {}
140 /** Called on failure to parse [source]. */ 34 /** Called on failure to parse [source]. */
141 void fail(String source, int position, String message) {} 35 void fail(String source, int position, String message) {}
142 } 36 }
143 37
144 /** 38 /**
145 * A [JsonListener] that builds data objects from the parser events. 39 * A [JsonListener] that builds data objects from the parser events.
146 * 40 *
147 * This is a simple stack-based object builder. It keeps the most recently 41 * This is a simple stack-based object builder. It keeps the most recently
148 * seen value in a variable, and uses it depending on the following event. 42 * seen value in a variable, and uses it depending on the following event.
149 */ 43 */
150 class BuildJsonListener extends JsonListener { 44 class _BuildJsonListener extends _JsonListener {
151 /** 45 /**
152 * Stack used to handle nested containers. 46 * Stack used to handle nested containers.
153 * 47 *
154 * The current container is pushed on the stack when a new one is 48 * The current container is pushed on the stack when a new one is
155 * started. If the container is a [Map], there is also a current [key] 49 * started. If the container is a [Map], there is also a current [key]
156 * which is also stored on the stack. 50 * which is also stored on the stack.
157 */ 51 */
158 List stack = []; 52 List stack = [];
159 /** The current [Map] or [List] being built. */ 53 /** The current [Map] or [List] being built. */
160 var currentContainer; 54 var currentContainer;
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
216 popContainer(); 110 popContainer();
217 } 111 }
218 112
219 /** Read out the final result of parsing a JSON string. */ 113 /** Read out the final result of parsing a JSON string. */
220 get result { 114 get result {
221 assert(currentContainer == null); 115 assert(currentContainer == null);
222 return value; 116 return value;
223 } 117 }
224 } 118 }
225 119
226 typedef _Reviver(var key, var value); 120 class _ReviverJsonListener extends _BuildJsonListener {
227
228 class ReviverJsonListener extends BuildJsonListener {
229 final _Reviver reviver; 121 final _Reviver reviver;
230 ReviverJsonListener(reviver(key, value)) : this.reviver = reviver; 122 _ReviverJsonListener(reviver(key, value)) : this.reviver = reviver;
231 123
232 void arrayElement() { 124 void arrayElement() {
233 List list = currentContainer; 125 List list = currentContainer;
234 value = reviver(list.length, value); 126 value = reviver(list.length, value);
235 super.arrayElement(); 127 super.arrayElement();
236 } 128 }
237 129
238 void propertyValue() { 130 void propertyValue() {
239 value = reviver(key, value); 131 value = reviver(key, value);
240 super.propertyValue(); 132 super.propertyValue();
241 } 133 }
242 134
243 get result { 135 get result {
244 return reviver("", value); 136 return reviver("", value);
245 } 137 }
246 } 138 }
247 139
248 class JsonParser { 140 class _JsonParser {
249 // A simple non-recursive state-based parser for JSON. 141 // A simple non-recursive state-based parser for JSON.
250 // 142 //
251 // Literal values accepted in states ARRAY_EMPTY, ARRAY_COMMA, OBJECT_COLON 143 // Literal values accepted in states ARRAY_EMPTY, ARRAY_COMMA, OBJECT_COLON
252 // and strings also in OBJECT_EMPTY, OBJECT_COMMA. 144 // and strings also in OBJECT_EMPTY, OBJECT_COMMA.
253 // VALUE STRING : , } ] Transitions to 145 // VALUE STRING : , } ] Transitions to
254 // EMPTY X X -> END 146 // EMPTY X X -> END
255 // ARRAY_EMPTY X X @ -> ARRAY_VALUE / pop 147 // ARRAY_EMPTY X X @ -> ARRAY_VALUE / pop
256 // ARRAY_VALUE @ @ -> ARRAY_COMMA / pop 148 // ARRAY_VALUE @ @ -> ARRAY_COMMA / pop
257 // ARRAY_COMMA X X -> ARRAY_VALUE 149 // ARRAY_COMMA X X -> ARRAY_VALUE
258 // OBJECT_EMPTY X @ -> OBJECT_KEY / pop 150 // OBJECT_EMPTY X @ -> OBJECT_KEY / pop
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after
330 static const int CHAR_l = 0x6c; 222 static const int CHAR_l = 0x6c;
331 static const int CHAR_n = 0x6e; 223 static const int CHAR_n = 0x6e;
332 static const int CHAR_r = 0x72; 224 static const int CHAR_r = 0x72;
333 static const int CHAR_s = 0x73; 225 static const int CHAR_s = 0x73;
334 static const int CHAR_t = 0x74; 226 static const int CHAR_t = 0x74;
335 static const int CHAR_u = 0x75; 227 static const int CHAR_u = 0x75;
336 static const int LBRACE = 0x7b; 228 static const int LBRACE = 0x7b;
337 static const int RBRACE = 0x7d; 229 static const int RBRACE = 0x7d;
338 230
339 final String source; 231 final String source;
340 final JsonListener listener; 232 final _JsonListener listener;
341 JsonParser(this.source, this.listener); 233 _JsonParser(this.source, this.listener);
342 234
343 /** Parses [source], or throws if it fails. */ 235 /** Parses [source], or throws if it fails. */
344 void parse() { 236 void parse() {
345 final List<int> states = <int>[]; 237 final List<int> states = <int>[];
346 int state = STATE_INITIAL; 238 int state = STATE_INITIAL;
347 int position = 0; 239 int position = 0;
348 int length = source.length; 240 int length = source.length;
349 while (position < length) { 241 while (position < length) {
350 int char = source.codeUnitAt(position); 242 int char = source.codeUnitAt(position);
351 switch (char) { 243 switch (char) {
(...skipping 303 matching lines...) Expand 10 before | Expand all | Expand 10 after
655 String slice; 547 String slice;
656 int sliceEnd = position + 20; 548 int sliceEnd = position + 20;
657 if (sliceEnd > source.length) { 549 if (sliceEnd > source.length) {
658 slice = "'${source.substring(position)}'"; 550 slice = "'${source.substring(position)}'";
659 } else { 551 } else {
660 slice = "'${source.substring(position, sliceEnd)}...'"; 552 slice = "'${source.substring(position, sliceEnd)}...'";
661 } 553 }
662 throw new FormatException("Unexpected character at $position: $slice"); 554 throw new FormatException("Unexpected character at $position: $slice");
663 } 555 }
664 } 556 }
665
666
667 class _JsonStringifier {
668 StringSink sink;
669 List<Object> seen; // TODO: that should be identity set.
670
671 _JsonStringifier(this.sink) : seen = [];
672
673 static String stringify(final object) {
674 StringBuffer output = new StringBuffer();
675 _JsonStringifier stringifier = new _JsonStringifier(output);
676 stringifier.stringifyValue(object);
677 return output.toString();
678 }
679
680 static void printOn(final object, StringSink output) {
681 _JsonStringifier stringifier = new _JsonStringifier(output);
682 stringifier.stringifyValue(object);
683 }
684
685 static String numberToString(num x) {
686 return x.toString();
687 }
688
689 // ('0' + x) or ('a' + x - 10)
690 static int hexDigit(int x) => x < 10 ? 48 + x : 87 + x;
691
692 static void escape(StringSink sb, String s) {
693 final int length = s.length;
694 bool needsEscape = false;
695 final charCodes = new List<int>();
696 for (int i = 0; i < length; i++) {
697 int charCode = s.codeUnitAt(i);
698 if (charCode < 32) {
699 needsEscape = true;
700 charCodes.add(JsonParser.BACKSLASH);
701 switch (charCode) {
702 case JsonParser.BACKSPACE:
703 charCodes.add(JsonParser.CHAR_b);
704 break;
705 case JsonParser.TAB:
706 charCodes.add(JsonParser.CHAR_t);
707 break;
708 case JsonParser.NEWLINE:
709 charCodes.add(JsonParser.CHAR_n);
710 break;
711 case JsonParser.FORM_FEED:
712 charCodes.add(JsonParser.CHAR_f);
713 break;
714 case JsonParser.CARRIAGE_RETURN:
715 charCodes.add(JsonParser.CHAR_r);
716 break;
717 default:
718 charCodes.add(JsonParser.CHAR_u);
719 charCodes.add(hexDigit((charCode >> 12) & 0xf));
720 charCodes.add(hexDigit((charCode >> 8) & 0xf));
721 charCodes.add(hexDigit((charCode >> 4) & 0xf));
722 charCodes.add(hexDigit(charCode & 0xf));
723 break;
724 }
725 } else if (charCode == JsonParser.QUOTE ||
726 charCode == JsonParser.BACKSLASH) {
727 needsEscape = true;
728 charCodes.add(JsonParser.BACKSLASH);
729 charCodes.add(charCode);
730 } else {
731 charCodes.add(charCode);
732 }
733 }
734 sb.write(needsEscape ? new String.fromCharCodes(charCodes) : s);
735 }
736
737 void checkCycle(final object) {
738 // TODO: use Iterables.
739 for (int i = 0; i < seen.length; i++) {
740 if (identical(seen[i], object)) {
741 throw new JsonCyclicError(object);
742 }
743 }
744 seen.add(object);
745 }
746
747 void stringifyValue(final object) {
748 // Tries stringifying object directly. If it's not a simple value, List or
749 // Map, call toJson() to get a custom representation and try serializing
750 // that.
751 if (!stringifyJsonValue(object)) {
752 checkCycle(object);
753 try {
754 var customJson = object.toJson();
755 if (!stringifyJsonValue(customJson)) {
756 throw new JsonUnsupportedObjectError(object);
757 }
758 seen.removeLast();
759 } catch (e) {
760 throw new JsonUnsupportedObjectError(object, cause: e);
761 }
762 }
763 }
764
765 /**
766 * Serializes a [num], [String], [bool], [Null], [List] or [Map] value.
767 *
768 * Returns true if the value is one of these types, and false if not.
769 * If a value is both a [List] and a [Map], it's serialized as a [List].
770 */
771 bool stringifyJsonValue(final object) {
772 if (object is num) {
773 // TODO: use writeOn.
774 sink.write(numberToString(object));
775 return true;
776 } else if (identical(object, true)) {
777 sink.write('true');
778 return true;
779 } else if (identical(object, false)) {
780 sink.write('false');
781 return true;
782 } else if (object == null) {
783 sink.write('null');
784 return true;
785 } else if (object is String) {
786 sink.write('"');
787 escape(sink, object);
788 sink.write('"');
789 return true;
790 } else if (object is List) {
791 checkCycle(object);
792 List a = object;
793 sink.write('[');
794 if (a.length > 0) {
795 stringifyValue(a[0]);
796 // TODO: switch to Iterables.
797 for (int i = 1; i < a.length; i++) {
798 sink.write(',');
799 stringifyValue(a[i]);
800 }
801 }
802 sink.write(']');
803 seen.removeLast();
804 return true;
805 } else if (object is Map) {
806 checkCycle(object);
807 Map<String, Object> m = object;
808 sink.write('{');
809 bool first = true;
810 m.forEach((String key, Object value) {
811 if (!first) {
812 sink.write(',"');
813 } else {
814 sink.write('"');
815 }
816 escape(sink, key);
817 sink.write('":');
818 stringifyValue(value);
819 first = false;
820 });
821 sink.write('}');
822 seen.removeLast();
823 return true;
824 } else {
825 return false;
826 }
827 }
828 }
OLDNEW
« no previous file with comments | « no previous file | runtime/lib/convert_sources.gypi » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698