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