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 |