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

Unified Diff: sdk/lib/convert/json.dart

Issue 40323002: Remove dart:json (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Bad merge in convert/json.dart, a few other bugs. Updated co19 expectations. Created 7 years, 2 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « sdk/lib/convert/convert.dart ('k') | sdk/lib/json/json.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: sdk/lib/convert/json.dart
diff --git a/sdk/lib/convert/json.dart b/sdk/lib/convert/json.dart
index 9780200836a060e892e8ef7644df49c4dda5e5b2..be503b0b92d5c9abc13f3a4bd765b7edd33b7b7e 100644
--- a/sdk/lib/convert/json.dart
+++ b/sdk/lib/convert/json.dart
@@ -17,16 +17,16 @@ part of dart.convert;
class JsonUnsupportedObjectError extends Error {
/** The object that could not be serialized. */
final unsupportedObject;
- /** The exception thrown by object's [:toJson:] method, if any. */
+ /** The exception thrown when trying to convert the object. */
final cause;
JsonUnsupportedObjectError(this.unsupportedObject, { this.cause });
String toString() {
if (cause != null) {
- return "Calling toJson method on object failed.";
+ return "Converting object to an encodable object failed.";
} else {
- return "Object toJson method returns non-serializable value.";
+ return "Converting object did not return an encodable object.";
}
}
}
@@ -86,8 +86,9 @@ class JsonCodec extends Codec<Object, String> {
*
* The default [reviver] (when not provided) is the identity function.
*/
- Object decode(String str, {reviver(var key, var value)}) {
- return new JsonDecoder(reviver).convert(str);
+ Object decode(String source, {reviver(var key, var value)}) {
+ if (reviver == null) return decoder.convert(source);
+ return new JsonDecoder(reviver).convert(source);
}
/**
@@ -102,11 +103,12 @@ class JsonCodec extends Codec<Object, String> {
* unencodable object.
*/
Object encode(Object value, {toEncodable(var object)}) {
+ if (toEncodable == null) return encoder.convert(value);
return new JsonEncoder(toEncodable).convert(value);
}
- JsonEncoder get encoder => new JsonEncoder();
- JsonDecoder get decoder => new JsonDecoder(null);
+ JsonEncoder get encoder => const JsonEncoder();
+ JsonDecoder get decoder => const JsonDecoder(null);
}
typedef _Reviver(var key, var value);
@@ -115,9 +117,9 @@ class _ReviverJsonCodec extends JsonCodec {
final _Reviver _reviver;
_ReviverJsonCodec(this._reviver);
- Object decode(String str, {reviver(var key, var value)}) {
+ Object decode(String source, {reviver(var key, var value)}) {
if (reviver == null) reviver = _reviver;
- return new JsonDecoder(reviver).convert(str);
+ return new JsonDecoder(reviver).convert(source);
}
JsonDecoder get decoder => new JsonDecoder(_reviver);
@@ -141,7 +143,7 @@ class JsonEncoder extends Converter<Object, String> {
* If [toEncodable] is omitted, it defaults to calling `.toJson()` on
* the object.
*/
- JsonEncoder([Object toEncodable(Object nonSerializable)])
+ const JsonEncoder([Object toEncodable(Object nonSerializable)])
: this._toEncodableFunction = toEncodable;
/**
@@ -171,7 +173,8 @@ class JsonEncoder extends Converter<Object, String> {
* the JSON text for it. I.e., if an object changes after it is first
* serialized, the new values may or may not be reflected in the result.
*/
- String convert(Object o) => OLD_JSON_LIB.stringify(o, _toEncodableFunction);
+ String convert(Object o) =>
+ _JsonStringifier.stringify(o, _toEncodableFunction);
/**
* Starts a chunked conversion.
@@ -219,7 +222,7 @@ class _JsonEncoderSink extends ChunkedConversionSink<Object> {
}
_isDone = true;
ClosableStringSink stringSink = _sink.asStringSink();
- OLD_JSON_LIB.printOn(o, stringSink, _toEncodableFunction);
+ _JsonStringifier.printOn(o, stringSink, _toEncodableFunction);
stringSink.close();
}
@@ -236,7 +239,7 @@ class JsonDecoder extends Converter<String, Object> {
*
* The [reviver] may be `null`.
*/
- JsonDecoder(reviver(var key, var value)) : this._reviver = reviver;
+ const JsonDecoder(reviver(var key, var value)) : this._reviver = reviver;
/**
* Converts the given JSON-string [input] to its corresponding object.
@@ -297,3 +300,185 @@ class _JsonDecoderSink extends _StringSinkConversionSink {
// Internal optimized JSON parsing implementation.
external _parseJson(String source, reviver(key, value));
+
+
+// Implementation of encoder/stringifier.
+
+Object _defaultToEncodable(object) => object.toJson();
+
+class _JsonStringifier {
+ // Character code constants.
+ static const int BACKSPACE = 0x08;
+ static const int TAB = 0x09;
+ static const int NEWLINE = 0x0a;
+ static const int CARRIAGE_RETURN = 0x0d;
+ static const int FORM_FEED = 0x0c;
+ static const int QUOTE = 0x22;
+ static const int BACKSLASH = 0x5c;
+ static const int CHAR_b = 0x62;
+ static const int CHAR_f = 0x66;
+ static const int CHAR_n = 0x6e;
+ static const int CHAR_r = 0x72;
+ static const int CHAR_t = 0x74;
+ static const int CHAR_u = 0x75;
+
+ final Function toEncodable;
+ final StringSink sink;
+ final Set<Object> seen;
+
+ _JsonStringifier(this.sink, this.toEncodable)
+ : this.seen = new HashSet.identity();
+
+ static String stringify(final object, toEncodable(object)) {
+ if (toEncodable == null) toEncodable = _defaultToEncodable;
+ StringBuffer output = new StringBuffer();
+ _JsonStringifier stringifier = new _JsonStringifier(output, toEncodable);
+ stringifier.stringifyValue(object);
+ return output.toString();
+ }
+
+ static void printOn(final object, StringSink output, toEncodable(object)) {
+ _JsonStringifier stringifier = new _JsonStringifier(output, toEncodable);
+ stringifier.stringifyValue(object);
+ }
+
+ static String numberToString(num x) {
+ return x.toString();
+ }
+
+ // ('0' + x) or ('a' + x - 10)
+ static int hexDigit(int x) => x < 10 ? 48 + x : 87 + x;
+
+ static void escape(StringSink sb, String s) {
+ final int length = s.length;
+ bool needsEscape = false;
+ final charCodes = new List<int>();
+ for (int i = 0; i < length; i++) {
+ int charCode = s.codeUnitAt(i);
+ if (charCode < 32) {
+ needsEscape = true;
+ charCodes.add(BACKSLASH);
+ switch (charCode) {
+ case BACKSPACE:
+ charCodes.add(CHAR_b);
+ break;
+ case TAB:
+ charCodes.add(CHAR_t);
+ break;
+ case NEWLINE:
+ charCodes.add(CHAR_n);
+ break;
+ case FORM_FEED:
+ charCodes.add(CHAR_f);
+ break;
+ case CARRIAGE_RETURN:
+ charCodes.add(CHAR_r);
+ break;
+ default:
+ charCodes.add(CHAR_u);
+ charCodes.add(hexDigit((charCode >> 12) & 0xf));
+ charCodes.add(hexDigit((charCode >> 8) & 0xf));
+ charCodes.add(hexDigit((charCode >> 4) & 0xf));
+ charCodes.add(hexDigit(charCode & 0xf));
+ break;
+ }
+ } else if (charCode == QUOTE || charCode == BACKSLASH) {
+ needsEscape = true;
+ charCodes.add(BACKSLASH);
+ charCodes.add(charCode);
+ } else {
+ charCodes.add(charCode);
+ }
+ }
+ sb.write(needsEscape ? new String.fromCharCodes(charCodes) : s);
+ }
+
+ void checkCycle(final object) {
+ if (seen.contains(object)) {
+ throw new JsonCyclicError(object);
+ }
+ seen.add(object);
+ }
+
+ void stringifyValue(final object) {
+ // Tries stringifying object directly. If it's not a simple value, List or
+ // Map, call toJson() to get a custom representation and try serializing
+ // that.
+ if (!stringifyJsonValue(object)) {
+ checkCycle(object);
+ try {
+ var customJson = toEncodable(object);
+ if (!stringifyJsonValue(customJson)) {
+ throw new JsonUnsupportedObjectError(object);
+ }
+ seen.remove(object);
+ } catch (e) {
+ throw new JsonUnsupportedObjectError(object, cause: e);
+ }
+ }
+ }
+
+ /**
+ * Serializes a [num], [String], [bool], [Null], [List] or [Map] value.
+ *
+ * Returns true if the value is one of these types, and false if not.
+ * If a value is both a [List] and a [Map], it's serialized as a [List].
+ */
+ bool stringifyJsonValue(final object) {
+ if (object is num) {
+ // TODO: use writeOn.
+ sink.write(numberToString(object));
+ return true;
+ } else if (identical(object, true)) {
+ sink.write('true');
+ return true;
+ } else if (identical(object, false)) {
+ sink.write('false');
+ return true;
+ } else if (object == null) {
+ sink.write('null');
+ return true;
+ } else if (object is String) {
+ sink.write('"');
+ escape(sink, object);
+ sink.write('"');
+ return true;
+ } else if (object is List) {
+ checkCycle(object);
+ List a = object;
+ sink.write('[');
+ if (a.length > 0) {
+ stringifyValue(a[0]);
+ // TODO: switch to Iterables.
+ for (int i = 1; i < a.length; i++) {
+ sink.write(',');
+ stringifyValue(a[i]);
+ }
+ }
+ sink.write(']');
+ seen.remove(object);
+ return true;
+ } else if (object is Map) {
+ checkCycle(object);
+ Map<String, Object> m = object;
+ sink.write('{');
+ bool first = true;
+ m.forEach((String key, Object value) {
+ if (!first) {
+ sink.write(',"');
+ } else {
+ sink.write('"');
+ }
+ escape(sink, key);
+ sink.write('":');
+ stringifyValue(value);
+ first = false;
+ });
+ sink.write('}');
+ seen.remove(object);
+ return true;
+ } else {
+ return false;
+ }
+ }
+}
« no previous file with comments | « sdk/lib/convert/convert.dart ('k') | sdk/lib/json/json.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698