Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2013, 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 part of dart.convert; | 5 part of dart.convert; |
| 6 | 6 |
| 7 /** | 7 /** |
| 8 * Error thrown by JSON serialization if an object cannot be serialized. | 8 * Error thrown by JSON serialization if an object cannot be serialized. |
| 9 * | 9 * |
| 10 * The [unsupportedObject] field holds that object that failed to be serialized. | 10 * The [unsupportedObject] field holds that object that failed to be serialized. |
| (...skipping 139 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 150 JsonDecoder get decoder { | 150 JsonDecoder get decoder { |
| 151 if (_reviver == null) return const JsonDecoder(); | 151 if (_reviver == null) return const JsonDecoder(); |
| 152 return new JsonDecoder(_reviver); | 152 return new JsonDecoder(_reviver); |
| 153 } | 153 } |
| 154 } | 154 } |
| 155 | 155 |
| 156 /** | 156 /** |
| 157 * This class converts JSON objects to strings. | 157 * This class converts JSON objects to strings. |
| 158 */ | 158 */ |
| 159 class JsonEncoder extends Converter<Object, String> { | 159 class JsonEncoder extends Converter<Object, String> { |
| 160 /** | |
| 161 * How many spaces each level of the JSON output is indented. | |
| 162 * | |
| 163 * If `null`, the output is encoded in a single line. | |
| 164 */ | |
| 165 final int indent; | |
|
Lasse Reichstein Nielsen
2014/04/03 18:18:17
Consider letting "indent" be a string which will b
| |
| 166 | |
| 160 final _toEncodableFunction; | 167 final _toEncodableFunction; |
| 161 | 168 |
| 162 /** | 169 /** |
| 163 * Creates a JSON encoder. | 170 * Creates a JSON encoder. |
| 164 * | 171 * |
| 165 * The JSON encoder handles numbers, strings, booleans, null, lists and | 172 * The JSON encoder handles numbers, strings, booleans, null, lists and |
| 166 * maps directly. | 173 * maps directly. |
| 167 * | 174 * |
| 168 * Any other object is attempted converted by [toEncodable] to an | 175 * Any other object is attempted converted by [toEncodable] to an |
| 169 * object that is of one of the convertible types. | 176 * object that is of one of the convertible types. |
| 170 * | 177 * |
| 171 * If [toEncodable] is omitted, it defaults to calling `.toJson()` on | 178 * If [toEncodable] is omitted, it defaults to calling `.toJson()` on |
| 172 * the object. | 179 * the object. |
| 173 */ | 180 */ |
| 174 const JsonEncoder([Object toEncodable(Object nonSerializable)]) | 181 const JsonEncoder([Object toEncodable(Object nonSerializable)]) |
| 175 : this._toEncodableFunction = toEncodable; | 182 : this.indent = null, |
| 183 this._toEncodableFunction = toEncodable; | |
| 184 | |
| 185 /** | |
| 186 * Creates a JSON encoder with each output level having a specified [indent]. | |
| 187 * | |
| 188 * An encoder with a `null` [indent] encodes objects onto a single line. | |
| 189 * | |
| 190 * The JSON encoder handles numbers, strings, booleans, null, lists and | |
| 191 * maps directly. | |
| 192 * | |
| 193 * Any other object is attempted converted by [toEncodable] to an | |
| 194 * object that is of one of the convertible types. | |
| 195 * | |
| 196 * If [toEncodable] is omitted, it defaults to calling `.toJson()` on | |
| 197 * the object. | |
| 198 */ | |
| 199 const JsonEncoder.withIndent(int indent, | |
| 200 [Object toEncodable(Object nonSerializable)]) | |
| 201 : this.indent = indent != null && indent < 0 ? 0 : indent, | |
| 202 this._toEncodableFunction = toEncodable; | |
| 176 | 203 |
| 177 /** | 204 /** |
| 178 * Converts the given object [o] to its JSON representation. | 205 * Converts the given object [o] to its JSON representation. |
| 179 * | 206 * |
| 180 * Directly serializable values are [num], [String], [bool], and [Null], as | 207 * Directly serializable values are [num], [String], [bool], and [Null], as |
| 181 * well as some [List] and [Map] values. | 208 * well as some [List] and [Map] values. |
| 182 * For [List], the elements must all be serializable. | 209 * For [List], the elements must all be serializable. |
| 183 * For [Map], the keys must be [String] and the values must be serializable. | 210 * For [Map], the keys must be [String] and the values must be serializable. |
| 184 * | 211 * |
| 185 * If a value is any other type is attempted serialized, the conversion | 212 * If a value is any other type is attempted serialized, the conversion |
| 186 * function provided in the constructor is invoked with the object as argument | 213 * function provided in the constructor is invoked with the object as argument |
| 187 * and the result, which must be a directly serializable value, | 214 * and the result, which must be a directly serializable value, |
| 188 * is serialized instead of the original value. | 215 * is serialized instead of the original value. |
| 189 * | 216 * |
| 190 * If the conversion throws, or returns a value that is not directly | 217 * If the conversion throws, or returns a value that is not directly |
| 191 * serializable, a [JsonUnsupportedObjectError] exception is thrown. | 218 * serializable, a [JsonUnsupportedObjectError] exception is thrown. |
| 192 * If the call throws, the error is caught and stored in the | 219 * If the call throws, the error is caught and stored in the |
| 193 * [JsonUnsupportedObjectError]'s [:cause:] field. | 220 * [JsonUnsupportedObjectError]'s [:cause:] field. |
| 194 * | 221 * |
| 195 * If a [List] or [Map] contains a reference to itself, directly or through | 222 * If a [List] or [Map] contains a reference to itself, directly or through |
| 196 * other lists or maps, it cannot be serialized and a [JsonCyclicError] is | 223 * other lists or maps, it cannot be serialized and a [JsonCyclicError] is |
| 197 * thrown. | 224 * thrown. |
| 198 * | 225 * |
| 199 * Json Objects should not change during serialization. | 226 * Json Objects should not change during serialization. |
| 200 * If an object is serialized more than once, [stringify] is allowed to cache | 227 * If an object is serialized more than once, [stringify] is allowed to cache |
| 201 * the JSON text for it. I.e., if an object changes after it is first | 228 * the JSON text for it. I.e., if an object changes after it is first |
| 202 * serialized, the new values may or may not be reflected in the result. | 229 * serialized, the new values may or may not be reflected in the result. |
| 203 */ | 230 */ |
| 204 String convert(Object o) => | 231 String convert(Object o) => |
| 205 _JsonStringifier.stringify(o, _toEncodableFunction); | 232 _JsonStringifier.stringify(o, _toEncodableFunction, indent); |
| 206 | 233 |
| 207 /** | 234 /** |
| 208 * Starts a chunked conversion. | 235 * Starts a chunked conversion. |
| 209 * | 236 * |
| 210 * The converter works more efficiently if the given [sink] is a | 237 * The converter works more efficiently if the given [sink] is a |
| 211 * [StringConversionSink]. | 238 * [StringConversionSink]. |
| 212 * | 239 * |
| 213 * Returns a chunked-conversion sink that accepts at most one object. It is | 240 * Returns a chunked-conversion sink that accepts at most one object. It is |
| 214 * an error to invoke `add` more than once on the returned sink. | 241 * an error to invoke `add` more than once on the returned sink. |
| 215 */ | 242 */ |
| 216 ChunkedConversionSink<Object> startChunkedConversion(Sink<String> sink) { | 243 ChunkedConversionSink<Object> startChunkedConversion(Sink<String> sink) { |
| 217 if (sink is! StringConversionSink) { | 244 if (sink is! StringConversionSink) { |
| 218 sink = new StringConversionSink.from(sink); | 245 sink = new StringConversionSink.from(sink); |
| 219 } | 246 } |
| 220 return new _JsonEncoderSink(sink, _toEncodableFunction); | 247 return new _JsonEncoderSink(sink, _toEncodableFunction, indent); |
| 221 } | 248 } |
| 222 | 249 |
| 223 // Override the base-classes bind, to provide a better type. | 250 // Override the base-classes bind, to provide a better type. |
| 224 Stream<String> bind(Stream<Object> stream) => super.bind(stream); | 251 Stream<String> bind(Stream<Object> stream) => super.bind(stream); |
| 225 } | 252 } |
| 226 | 253 |
| 227 /** | 254 /** |
| 228 * Implements the chunked conversion from object to its JSON representation. | 255 * Implements the chunked conversion from object to its JSON representation. |
| 229 * | 256 * |
| 230 * The sink only accepts one value, but will produce output in a chunked way. | 257 * The sink only accepts one value, but will produce output in a chunked way. |
| 231 */ | 258 */ |
| 232 class _JsonEncoderSink extends ChunkedConversionSink<Object> { | 259 class _JsonEncoderSink extends ChunkedConversionSink<Object> { |
| 260 final int _indent; | |
| 233 final Function _toEncodableFunction; | 261 final Function _toEncodableFunction; |
| 234 final StringConversionSink _sink; | 262 final StringConversionSink _sink; |
| 235 bool _isDone = false; | 263 bool _isDone = false; |
| 236 | 264 |
| 237 _JsonEncoderSink(this._sink, this._toEncodableFunction); | 265 _JsonEncoderSink(this._sink, this._toEncodableFunction, this._indent); |
| 238 | 266 |
| 239 /** | 267 /** |
| 240 * Encodes the given object [o]. | 268 * Encodes the given object [o]. |
| 241 * | 269 * |
| 242 * It is an error to invoke this method more than once on any instance. While | 270 * It is an error to invoke this method more than once on any instance. While |
| 243 * this makes the input effectly non-chunked the output will be generated in | 271 * this makes the input effectly non-chunked the output will be generated in |
| 244 * a chunked way. | 272 * a chunked way. |
| 245 */ | 273 */ |
| 246 void add(Object o) { | 274 void add(Object o) { |
| 247 if (_isDone) { | 275 if (_isDone) { |
| 248 throw new StateError("Only one call to add allowed"); | 276 throw new StateError("Only one call to add allowed"); |
| 249 } | 277 } |
| 250 _isDone = true; | 278 _isDone = true; |
| 251 ClosableStringSink stringSink = _sink.asStringSink(); | 279 ClosableStringSink stringSink = _sink.asStringSink(); |
| 252 _JsonStringifier.printOn(o, stringSink, _toEncodableFunction); | 280 _JsonStringifier.printOn(o, stringSink, _toEncodableFunction, _indent); |
| 253 stringSink.close(); | 281 stringSink.close(); |
| 254 } | 282 } |
| 255 | 283 |
| 256 void close() { /* do nothing */ } | 284 void close() { /* do nothing */ } |
| 257 } | 285 } |
| 258 | 286 |
| 259 /** | 287 /** |
| 260 * This class parses JSON strings and builds the corresponding objects. | 288 * This class parses JSON strings and builds the corresponding objects. |
| 261 */ | 289 */ |
| 262 class JsonDecoder extends Converter<String, Object> { | 290 class JsonDecoder extends Converter<String, Object> { |
| (...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 346 static const int CHAR_f = 0x66; | 374 static const int CHAR_f = 0x66; |
| 347 static const int CHAR_n = 0x6e; | 375 static const int CHAR_n = 0x6e; |
| 348 static const int CHAR_r = 0x72; | 376 static const int CHAR_r = 0x72; |
| 349 static const int CHAR_t = 0x74; | 377 static const int CHAR_t = 0x74; |
| 350 static const int CHAR_u = 0x75; | 378 static const int CHAR_u = 0x75; |
| 351 | 379 |
| 352 final Function _toEncodable; | 380 final Function _toEncodable; |
| 353 final StringSink _sink; | 381 final StringSink _sink; |
| 354 final List _seen; | 382 final List _seen; |
| 355 | 383 |
| 356 _JsonStringifier(this._sink, this._toEncodable) | 384 factory _JsonStringifier(StringSink sink, Function toEncodable, int indent) { |
| 385 if (indent == null) return new _JsonStringifier._(sink, toEncodable); | |
| 386 return new _JsonStringifierPretty(sink, toEncodable, indent); | |
| 387 } | |
| 388 | |
| 389 _JsonStringifier._(this._sink, this._toEncodable) | |
| 357 : this._seen = new List(); | 390 : this._seen = new List(); |
| 358 | 391 |
| 359 static String stringify(object, toEncodable(object)) { | 392 static String stringify(object, toEncodable(object), int indent) { |
| 360 if (toEncodable == null) toEncodable = _defaultToEncodable; | 393 if (toEncodable == null) toEncodable = _defaultToEncodable; |
| 361 StringBuffer output = new StringBuffer(); | 394 StringBuffer output = new StringBuffer(); |
| 362 printOn(object, output, toEncodable); | 395 printOn(object, output, toEncodable, indent); |
| 363 return output.toString(); | 396 return output.toString(); |
| 364 } | 397 } |
| 365 | 398 |
| 366 static void printOn(object, StringSink output, toEncodable(object)) { | 399 static void printOn(object, StringSink output, toEncodable(object), |
| 367 _JsonStringifier stringifier = new _JsonStringifier(output, toEncodable); | 400 int indent) { |
| 368 stringifier.stringifyValue(object); | 401 new _JsonStringifier(output, toEncodable, indent).stringifyValue(object); |
| 369 } | 402 } |
| 370 | 403 |
| 371 static String numberToString(num x) { | 404 static String numberToString(num x) { |
| 372 return x.toString(); | 405 return x.toString(); |
| 373 } | 406 } |
| 374 | 407 |
| 375 // ('0' + x) or ('a' + x - 10) | 408 // ('0' + x) or ('a' + x - 10) |
| 376 static int hexDigit(int x) => x < 10 ? 48 + x : 87 + x; | 409 static int hexDigit(int x) => x < 10 ? 48 + x : 87 + x; |
| 377 | 410 |
| 378 void escape(String s) { | 411 void escape(String s) { |
| (...skipping 129 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 508 return false; | 541 return false; |
| 509 } | 542 } |
| 510 } | 543 } |
| 511 | 544 |
| 512 void _removeSeen(object) { | 545 void _removeSeen(object) { |
| 513 assert(!_seen.isEmpty); | 546 assert(!_seen.isEmpty); |
| 514 assert(identical(_seen.last, object)); | 547 assert(identical(_seen.last, object)); |
| 515 _seen.removeLast(); | 548 _seen.removeLast(); |
| 516 } | 549 } |
| 517 } | 550 } |
| 551 | |
| 552 | |
| 553 class _JsonStringifierPretty extends _JsonStringifier { | |
| 554 | |
| 555 final String _indent; | |
| 556 | |
| 557 int _indentLevel = 0; | |
| 558 | |
| 559 _JsonStringifierPretty(_sink, _toEncodable, int indent) | |
| 560 : this._indent = _getIndent(indent), | |
| 561 super._(_sink, _toEncodable); | |
| 562 | |
| 563 static String _getIndent(int indent) { | |
| 564 if (indent < 0) throw new ArgumentError('indent cannot be negative'); | |
| 565 return ' ' * indent; | |
| 566 } | |
| 567 | |
| 568 void _write([String value = '']) { | |
| 569 _sink.write(_indent * _indentLevel); | |
| 570 _sink.write(value); | |
| 571 } | |
| 572 | |
| 573 /** | |
| 574 * Serializes a [num], [String], [bool], [Null], [List] or [Map] value. | |
| 575 * | |
| 576 * Returns true if the value is one of these types, and false if not. | |
| 577 * If a value is both a [List] and a [Map], it's serialized as a [List]. | |
| 578 */ | |
| 579 bool stringifyJsonValue(final object) { | |
| 580 if (object is List) { | |
| 581 checkCycle(object); | |
| 582 List a = object; | |
| 583 if (a.isEmpty) { | |
| 584 _sink.write('[]'); | |
| 585 } else { | |
| 586 _sink.writeln('['); | |
| 587 _indentLevel++; | |
| 588 _write(); | |
| 589 stringifyValue(a[0]); | |
| 590 for (int i = 1; i < a.length; i++) { | |
| 591 _sink.writeln(','); | |
| 592 _write(); | |
| 593 stringifyValue(a[i]); | |
| 594 } | |
| 595 _sink.writeln(); | |
| 596 _indentLevel--; | |
| 597 _write(']'); | |
| 598 } | |
| 599 _seen.remove(object); | |
| 600 return true; | |
| 601 } else if (object is Map) { | |
| 602 checkCycle(object); | |
| 603 Map<String, Object> m = object; | |
| 604 if (m.isEmpty) { | |
| 605 _sink.write('{}'); | |
| 606 } else { | |
| 607 _sink.write('{'); | |
| 608 _sink.writeln(); | |
| 609 _indentLevel++; | |
| 610 bool first = true; | |
| 611 m.forEach((String key, Object value) { | |
| 612 if (!first) { | |
| 613 _sink.writeln(','); | |
| 614 } | |
| 615 _write('"'); | |
| 616 escape(key); | |
| 617 _sink.write('": '); | |
| 618 stringifyValue(value); | |
| 619 first = false; | |
| 620 }); | |
| 621 _sink.writeln(); | |
| 622 _indentLevel--; | |
| 623 _write('}'); | |
| 624 } | |
| 625 _seen.remove(object); | |
| 626 return true; | |
| 627 } | |
| 628 return super.stringifyJsonValue(object); | |
| 629 } | |
| 630 } | |
| OLD | NEW |