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 |