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

Side by Side Diff: sdk/lib/json/json.dart

Issue 25548010: Make JSON encoder take extra function argument to use instead of toJson calls. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: 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 unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2012, 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 /** 5 /**
6 * Utilities for encoding and decoding JSON (JavaScript Object Notation) data. 6 * Utilities for encoding and decoding JSON (JavaScript Object Notation) data.
7 */ 7 */
8 8
9 library dart.json; 9 library dart.json;
10 10
11 import "dart:convert"; 11 import "dart:convert";
12 import "dart:collection" show HashSet;
12 export "dart:convert" show JsonUnsupportedObjectError, JsonCyclicError; 13 export "dart:convert" show JsonUnsupportedObjectError, JsonCyclicError;
13 14
14 // JSON parsing and serialization. 15 // JSON parsing and serialization.
15 16
16 /** 17 /**
17 * Parses [json] and build the corresponding parsed JSON value. 18 * Parses [json] and build the corresponding parsed JSON value.
18 * 19 *
19 * Parsed JSON values are of the types [num], [String], [bool], [Null], 20 * Parsed JSON values are of the types [num], [String], [bool], [Null],
20 * [List]s of parsed JSON values or [Map]s from [String] to parsed 21 * [List]s of parsed JSON values or [Map]s from [String] to parsed
21 * JSON values. 22 * JSON values.
(...skipping 15 matching lines...) Expand all
37 } 38 }
38 39
39 /** 40 /**
40 * Serializes [object] into a JSON string. 41 * Serializes [object] into a JSON string.
41 * 42 *
42 * Directly serializable values are [num], [String], [bool], and [Null], as well 43 * Directly serializable values are [num], [String], [bool], and [Null], as well
43 * as some [List] and [Map] values. 44 * as some [List] and [Map] values.
44 * For [List], the elements must all be serializable. 45 * For [List], the elements must all be serializable.
45 * For [Map], the keys must be [String] and the values must be serializable. 46 * For [Map], the keys must be [String] and the values must be serializable.
46 * 47 *
47 * If a value is any other type is attempted serialized, a "toJson()" method 48 * If a value is any other type is attempted serialized, the [convert] method
48 * is invoked on the object and the result, which must be a directly 49 * is called with the object as argument, and the result, which must be a
49 * serializable value, is serialized instead of the original value. 50 * directly serializable value, is serialized instead of the original value.
51 * If [convert] is omitted, the default is to call `object.toJson()` on the
52 * object.
50 * 53 *
51 * If the object does not support this method, throws, or returns a 54 * If the conversion throws, or returns a value that is not directly
52 * value that is not directly serializable, a [JsonUnsupportedObjectError] 55 * serializable, a [JsonUnsupportedObjectError] exception is thrown.
53 * exception is thrown. If the call throws (including the case where there 56 * If the call throws (including the case where there
54 * is no nullary "toJson" method, the error is caught and stored in the 57 * is no nullary "toJson" method), the error is caught and stored in the
55 * [JsonUnsupportedObjectError]'s [:cause:] field. 58 * [JsonUnsupportedObjectError]'s [:cause:] field.
56 * 59 *
57 * If a [List] or [Map] contains a reference to itself, directly or through 60 * If a [List] or [Map] contains a reference to itself, directly or through
58 * other lists or maps, it cannot be serialized and a [JsonCyclicError] is 61 * other lists or maps, it cannot be serialized and a [JsonCyclicError] is
59 * thrown. 62 * thrown.
60 * 63 *
61 * Json Objects should not change during serialization. 64 * The objects being serialized should not change during serialization.
62 * If an object is serialized more than once, [stringify] is allowed to cache 65 * If an object is serialized more than once, [stringify] is allowed to cache
63 * the JSON text for it. I.e., if an object changes after it is first 66 * the JSON text for it. I.e., if an object changes after it is first
64 * serialized, the new values may or may not be reflected in the result. 67 * serialized, the new values may or may not be reflected in the result.
65 */ 68 */
66 String stringify(Object object) { 69 String stringify(Object object, [convert(object)]) {
floitsch 2013/10/04 15:25:08 You don't have the latest version (which now has d
Lasse Reichstein Nielsen 2013/10/07 12:46:59 Merging .... please hold. :) I don't want to supp
67 return _JsonStringifier.stringify(object); 70 if (convert == null) convert = _defaultConvert;
71 return _JsonStringifier.stringify(object, convert);
68 } 72 }
69 73
70 /** 74 /**
71 * Serializes [object] into [output] stream. 75 * Serializes [object] into [output] stream.
72 * 76 *
73 * Performs the same operations as [stringify] but outputs the resulting 77 * Performs the same operations as [stringify] but outputs the resulting
74 * string to an existing [StringSink] instead of creating a new [String]. 78 * string to an existing [StringSink] instead of creating a new [String].
75 * 79 *
76 * If serialization fails by throwing, some data might have been added to 80 * If serialization fails by throwing, some data might have been added to
77 * [output], but it won't contain valid JSON text. 81 * [output], but it won't contain valid JSON text.
78 */ 82 */
79 void printOn(Object object, StringSink output) { 83 void printOn(Object object, StringSink output, [ convert(object) ]) {
80 return _JsonStringifier.printOn(object, output); 84 if (convert == null) convert = _defaultConvert;
85 return _JsonStringifier.printOn(object, output, convert);
81 } 86 }
82 87
83 //// Implementation /////////////////////////////////////////////////////////// 88 //// Implementation ///////////////////////////////////////////////////////////
84 89
85 // Simple API for JSON parsing. 90 // Simple API for JSON parsing.
86 91
87 abstract class JsonListener { 92 abstract class JsonListener {
88 void handleString(String value) {} 93 void handleString(String value) {}
89 void handleNumber(num value) {} 94 void handleNumber(num value) {}
90 void handleBool(bool value) {} 95 void handleBool(bool value) {}
(...skipping 525 matching lines...) Expand 10 before | Expand all | Expand 10 after
616 int sliceEnd = position + 20; 621 int sliceEnd = position + 20;
617 if (sliceEnd > source.length) { 622 if (sliceEnd > source.length) {
618 slice = "'${source.substring(position)}'"; 623 slice = "'${source.substring(position)}'";
619 } else { 624 } else {
620 slice = "'${source.substring(position, sliceEnd)}...'"; 625 slice = "'${source.substring(position, sliceEnd)}...'";
621 } 626 }
622 throw new FormatException("Unexpected character at $position: $slice"); 627 throw new FormatException("Unexpected character at $position: $slice");
623 } 628 }
624 } 629 }
625 630
631 Object _defaultConvert(object) => object.toJson();
626 632
627 class _JsonStringifier { 633 class _JsonStringifier {
628 StringSink sink; 634 final Function converter;
629 List<Object> seen; // TODO: that should be identity set. 635 final StringSink sink;
636 final Set<Object> seen;
630 637
631 _JsonStringifier(this.sink) : seen = []; 638 _JsonStringifier(this.sink, this.converter)
639 : this.seen = new HashSet.identity();
632 640
633 static String stringify(final object) { 641 static String stringify(final object, convert(object)) {
634 StringBuffer output = new StringBuffer(); 642 StringBuffer output = new StringBuffer();
635 _JsonStringifier stringifier = new _JsonStringifier(output); 643 _JsonStringifier stringifier = new _JsonStringifier(output, convert);
636 stringifier.stringifyValue(object); 644 stringifier.stringifyValue(object);
637 return output.toString(); 645 return output.toString();
638 } 646 }
639 647
640 static void printOn(final object, StringSink output) { 648 static void printOn(final object, StringSink output, convert(object)) {
641 _JsonStringifier stringifier = new _JsonStringifier(output); 649 _JsonStringifier stringifier = new _JsonStringifier(output, convert);
642 stringifier.stringifyValue(object); 650 stringifier.stringifyValue(object);
643 } 651 }
644 652
645 static String numberToString(num x) { 653 static String numberToString(num x) {
646 return x.toString(); 654 return x.toString();
647 } 655 }
648 656
649 // ('0' + x) or ('a' + x - 10) 657 // ('0' + x) or ('a' + x - 10)
650 static int hexDigit(int x) => x < 10 ? 48 + x : 87 + x; 658 static int hexDigit(int x) => x < 10 ? 48 + x : 87 + x;
651 659
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
688 charCodes.add(JsonParser.BACKSLASH); 696 charCodes.add(JsonParser.BACKSLASH);
689 charCodes.add(charCode); 697 charCodes.add(charCode);
690 } else { 698 } else {
691 charCodes.add(charCode); 699 charCodes.add(charCode);
692 } 700 }
693 } 701 }
694 sb.write(needsEscape ? new String.fromCharCodes(charCodes) : s); 702 sb.write(needsEscape ? new String.fromCharCodes(charCodes) : s);
695 } 703 }
696 704
697 void checkCycle(final object) { 705 void checkCycle(final object) {
698 // TODO: use Iterables. 706 if (seen.contains(object)) {
699 for (int i = 0; i < seen.length; i++) { 707 throw new JsonCyclicError(object);
700 if (identical(seen[i], object)) {
701 throw new JsonCyclicError(object);
702 }
703 } 708 }
704 seen.add(object); 709 seen.add(object);
705 } 710 }
706 711
707 void stringifyValue(final object) { 712 void stringifyValue(final object) {
708 // Tries stringifying object directly. If it's not a simple value, List or 713 // Tries stringifying object directly. If it's not a simple value, List or
709 // Map, call toJson() to get a custom representation and try serializing 714 // Map, call toJson() to get a custom representation and try serializing
710 // that. 715 // that.
711 if (!stringifyJsonValue(object)) { 716 if (!stringifyJsonValue(object)) {
712 checkCycle(object); 717 checkCycle(object);
713 try { 718 try {
714 var customJson = object.toJson(); 719 var customJson = converter(object);
715 if (!stringifyJsonValue(customJson)) { 720 if (!stringifyJsonValue(customJson)) {
716 throw new JsonUnsupportedObjectError(object); 721 throw new JsonUnsupportedObjectError(object);
717 } 722 }
718 seen.removeLast(); 723 seen.remove(object);
719 } catch (e) { 724 } catch (e) {
720 throw new JsonUnsupportedObjectError(object, cause: e); 725 throw new JsonUnsupportedObjectError(object, cause: e);
721 } 726 }
722 } 727 }
723 } 728 }
724 729
725 /** 730 /**
726 * Serializes a [num], [String], [bool], [Null], [List] or [Map] value. 731 * Serializes a [num], [String], [bool], [Null], [List] or [Map] value.
727 * 732 *
728 * Returns true if the value is one of these types, and false if not. 733 * Returns true if the value is one of these types, and false if not.
(...skipping 24 matching lines...) Expand all
753 sink.write('['); 758 sink.write('[');
754 if (a.length > 0) { 759 if (a.length > 0) {
755 stringifyValue(a[0]); 760 stringifyValue(a[0]);
756 // TODO: switch to Iterables. 761 // TODO: switch to Iterables.
757 for (int i = 1; i < a.length; i++) { 762 for (int i = 1; i < a.length; i++) {
758 sink.write(','); 763 sink.write(',');
759 stringifyValue(a[i]); 764 stringifyValue(a[i]);
760 } 765 }
761 } 766 }
762 sink.write(']'); 767 sink.write(']');
763 seen.removeLast(); 768 seen.remove(object);
764 return true; 769 return true;
765 } else if (object is Map) { 770 } else if (object is Map) {
766 checkCycle(object); 771 checkCycle(object);
767 Map<String, Object> m = object; 772 Map<String, Object> m = object;
768 sink.write('{'); 773 sink.write('{');
769 bool first = true; 774 bool first = true;
770 m.forEach((String key, Object value) { 775 m.forEach((String key, Object value) {
771 if (!first) { 776 if (!first) {
772 sink.write(',"'); 777 sink.write(',"');
773 } else { 778 } else {
774 sink.write('"'); 779 sink.write('"');
775 } 780 }
776 escape(sink, key); 781 escape(sink, key);
777 sink.write('":'); 782 sink.write('":');
778 stringifyValue(value); 783 stringifyValue(value);
779 first = false; 784 first = false;
780 }); 785 });
781 sink.write('}'); 786 sink.write('}');
782 seen.removeLast(); 787 seen.remove(object);
783 return true; 788 return true;
784 } else { 789 } else {
785 return false; 790 return false;
786 } 791 }
787 } 792 }
788 } 793 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698