OLD | NEW |
---|---|
1 part of serialization; | 1 part of serialization; |
2 | 2 |
3 /** | 3 /** |
4 * An abstract class for serialization formats. Subclasses define how data | 4 * An abstract class for serialization formats. Subclasses define how data |
5 * is read or written to a particular output mechanism. | 5 * is read or written to a particular output mechanism. |
6 */ | 6 */ |
7 abstract class Format { | 7 abstract class Format { |
8 /** | 8 /** |
9 * Return true if this format stores primitives in their own area and uses | 9 * Return true if this format stores primitives in their own area and uses |
10 * references to them (e.g. [SimpleFlatFormat]) and false if primitives | 10 * references to them (e.g. [SimpleFlatFormat]) and false if primitives |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
45 * is the [json] representation of a nested Map structure. The top level has | 45 * is the [json] representation of a nested Map structure. The top level has |
46 * 3 fields, "rules" which may hold a definition of the rules used, | 46 * 3 fields, "rules" which may hold a definition of the rules used, |
47 * "data" which holds the serialized data, and "roots", which holds | 47 * "data" which holds the serialized data, and "roots", which holds |
48 * [Reference] objects indicating the root objects. Note that roots are | 48 * [Reference] objects indicating the root objects. Note that roots are |
49 * necessary because the data is organized in the same way as the object | 49 * necessary because the data is organized in the same way as the object |
50 * structure, it's a list of lists holding self-contained maps which only | 50 * structure, it's a list of lists holding self-contained maps which only |
51 * refer to other parts via [Reference] objects. | 51 * refer to other parts via [Reference] objects. |
52 * This effectively defines a custom JSON serialization format, although | 52 * This effectively defines a custom JSON serialization format, although |
53 * the details of the format vary depending which rules were used. | 53 * the details of the format vary depending which rules were used. |
54 */ | 54 */ |
55 String generateOutput(Writer w) { | 55 generateOutput(Writer w) { |
justinfagnani
2013/02/09 21:21:56
Style nit: Why remove the type here? Since the met
Jennifer Messerly
2013/02/10 00:20:35
Style nit nit :)
If return type is "dynamic", we
Alan Knight
2013/02/11 23:38:42
Good point, changed this to return Map<String, dyn
| |
56 var result = { | 56 var result = { |
57 "rules" : w.serializedRules(), | 57 "rules" : w.serializedRules(), |
58 "data" : w.states, | 58 "data" : w.states, |
59 "roots" : w._rootReferences() | 59 "roots" : w._rootReferences() |
60 }; | 60 }; |
61 return json.stringify(result); | 61 return result; |
62 } | 62 } |
63 | 63 |
64 /** | 64 /** |
65 * Read a [json] encoded string representing serialized data in this format | 65 * Read a [json] compatible representation of serialized data in this format |
66 * and return the nested Map representation described in [generateOutput]. If | 66 * and return the nested Map representation described in [generateOutput]. If |
67 * the data also includes rule definitions, then these will replace the rules | 67 * the data also includes rule definitions, then these will replace the rules |
68 * in the [Serialization] for [reader]. | 68 * in the [Serialization] for [reader]. |
69 */ | 69 */ |
70 Map<String, dynamic> read(String input, Reader reader) { | 70 Map<String, dynamic> read(topLevel, Reader reader) { |
71 var topLevel = json.parse(input); | |
72 var ruleString = topLevel["rules"]; | 71 var ruleString = topLevel["rules"]; |
73 reader.readRules(ruleString); | 72 reader.readRules(ruleString); |
74 return topLevel; | 73 return topLevel; |
75 } | 74 } |
76 } | 75 } |
77 | 76 |
78 /** | 77 /** |
79 * A format for "normal" JSON representation of objects. It stores | 78 * A format for "normal" JSON representation of objects. It stores |
80 * the fields of the objects as nested maps, and doesn't allow cycles. This can | 79 * the fields of the objects as nested maps, and doesn't allow cycles. This can |
81 * be useful in talking to existing APIs that expect JSON format data. However, | 80 * be useful in talking to existing APIs that expect JSON format data. However, |
(...skipping 14 matching lines...) Expand all Loading... | |
96 final bool storeRoundTripInfo; | 95 final bool storeRoundTripInfo; |
97 | 96 |
98 /** | 97 /** |
99 * If we store the rule numbers, what key should we use to store them. | 98 * If we store the rule numbers, what key should we use to store them. |
100 */ | 99 */ |
101 String ruleIdentifier = "__rule"; | 100 String ruleIdentifier = "__rule"; |
102 | 101 |
103 SimpleJsonFormat({this.storeRoundTripInfo : false}); | 102 SimpleJsonFormat({this.storeRoundTripInfo : false}); |
104 | 103 |
105 /** | 104 /** |
106 * Generate output for this format from [w] and return it as a String which | 105 * Generate output for this format from [w] and return it as |
107 * is the [json] representation of a nested Map structure. | 106 * the [json] representation of a nested Map structure. |
108 */ | 107 */ |
109 String generateOutput(Writer w) { | 108 generateOutput(Writer w) { |
justinfagnani
2013/02/09 21:21:56
Map as the return type?
Jennifer Messerly
2013/02/10 00:20:35
I don't think it necessarily returns a map if not
Alan Knight
2013/02/11 23:38:42
It doesn't necessarily return a Map, part of the c
| |
110 jsonify(w); | 109 jsonify(w); |
111 return json.stringify(w.stateForReference(w._rootReferences().first)); | 110 var root = w._rootReferences().first; |
111 if (root is Reference) root = w.stateForReference(root); | |
112 if (w.selfDescribing && storeRoundTripInfo) { | |
113 root = { | |
114 "rules" : w.serializedRules(), | |
Jennifer Messerly
2013/02/08 04:04:38
I think I usually just indent this +2, since it ha
Alan Knight
2013/02/11 23:38:42
Because I made those be identifiers rather than li
| |
115 "data" : root | |
116 }; | |
117 } | |
118 return root; | |
112 } | 119 } |
113 | 120 |
114 /** | 121 /** |
115 * Convert the data generated by the rules to have nested maps instead | 122 * Convert the data generated by the rules to have nested maps instead |
116 * of Reference objects and to add rule numbers if [storeRoundTripInfo] | 123 * of Reference objects and to add rule numbers if [storeRoundTripInfo] |
117 * is true. | 124 * is true. |
118 */ | 125 */ |
119 jsonify(Writer w) { | 126 jsonify(Writer w) { |
120 for (var eachRule in w.rules) { | 127 for (var eachRule in w.rules) { |
121 var ruleData = w.states[eachRule.number]; | 128 var ruleData = w.states[eachRule.number]; |
(...skipping 21 matching lines...) Expand all Loading... | |
143 * For one particular entry, which is either a Map or a List, update it | 150 * For one particular entry, which is either a Map or a List, update it |
144 * to turn References into a nested List/Map. | 151 * to turn References into a nested List/Map. |
145 */ | 152 */ |
146 jsonifyEntry(map, Writer w) { | 153 jsonifyEntry(map, Writer w) { |
147 keysAndValues(map).forEach((key, value) { | 154 keysAndValues(map).forEach((key, value) { |
148 if (value is Reference) map[key] = w.stateForReference(value); | 155 if (value is Reference) map[key] = w.stateForReference(value); |
149 }); | 156 }); |
150 } | 157 } |
151 | 158 |
152 /** | 159 /** |
153 * Read a [json] encoded string representing serialized data in this format | 160 * Read a [json] encoded string representing serialized data in this format |
justinfagnani
2013/02/09 21:21:56
string->?
Alan Knight
2013/02/11 23:38:42
Done.
| |
154 * and return the Map representation that the reader expects, with top-level | 161 * and return the Map representation that the reader expects, with top-level |
155 * entries for "rules", "data", and "roots". Nested lists/maps will be | 162 * entries for "rules", "data", and "roots". Nested lists/maps will be |
156 * converted into Reference objects. Note that if the data was not written | 163 * converted into Reference objects. Note that if the data was not written |
157 * with [storeRoundTripInfo] true this will fail. | 164 * with [storeRoundTripInfo] true this will fail. |
158 */ | 165 */ |
159 Map<String, dynamic> read(String input, Reader r) { | 166 Map<String, dynamic> read(data, Reader reader) { |
160 var data = json.parse(input); | 167 var result = new Map(); |
161 var result = {}; | 168 bool looksLikeItHasOurExtraData = data is Map && |
162 result["rules"] = null; | 169 data.containsKey("rules") && data.containsKey("data"); |
163 var ruleData = | 170 var rules = looksLikeItHasOurExtraData ? data["rules"] : null; |
164 new List(r.serialization.rules.length).map((x) => []).toList(); | 171 reader.readRules(rules); |
justinfagnani
2013/02/09 21:21:56
what happens here if "rules" is just some user dat
Jennifer Messerly
2013/02/10 00:20:35
This is a good idea.
We should go even further IM
Alan Knight
2013/02/11 23:38:42
OK, I changed this to just look at the "selfDescri
| |
165 var top = recursivelyFixUp(data, r, ruleData); | 172 var ruleData = new List(reader.serialization.rules.length). |
173 map((x) => []).toList(); | |
174 // If our result was a map with rules and data, get the data part. Otherwise | |
175 // assume that the whole thing is the data. | |
176 var actualData = (rules == null) ? data : data["data"]; | |
177 var top = recursivelyFixUp(actualData, reader, ruleData); | |
166 result["data"] = ruleData; | 178 result["data"] = ruleData; |
167 result["roots"] = [top]; | 179 result["roots"] = [top]; |
168 return result; | 180 return result; |
169 } | 181 } |
170 | 182 |
171 /** | 183 /** |
172 * Convert nested references in [data] into [Reference] objects. | 184 * Convert nested references in [data] into [Reference] objects. |
173 */ | 185 */ |
174 recursivelyFixUp(data, Reader r, List result) { | 186 recursivelyFixUp(input, Reader r, List result) { |
187 var data = input; | |
175 if (isPrimitive(data)) { | 188 if (isPrimitive(data)) { |
176 result[r._primitiveRule().number].add(data); | 189 result[r._primitiveRule().number].add(data); |
177 return data; | 190 return data; |
178 } | 191 } |
179 var ruleNumber; | 192 var ruleNumber; |
193 // If we've added the rule number on as the last item in a list we have | |
194 // to get rid of it or it will be interpreted as extra data. For a map | |
195 // the library will be ok, but we need to get rid of the extra key before | |
196 // the data is shown to the user, so we destructively modify. | |
180 if (data is List) { | 197 if (data is List) { |
181 ruleNumber = data.removeLast(); | 198 ruleNumber = data.last; |
199 data = data.take(data.length -1); | |
182 } else if (data is Map) { | 200 } else if (data is Map) { |
183 ruleNumber = data.remove(ruleIdentifier); | 201 ruleNumber = data.remove(ruleIdentifier); |
184 } else { | 202 } else { |
185 throw new SerializationException("Invalid data format"); | 203 throw new SerializationException("Invalid data format"); |
186 } | 204 } |
187 var newData = mapValues(data, (x) => recursivelyFixUp(x, r, result)); | 205 // Do not use mappedBy or other lazy operations for this. They do not play |
206 // well with a function that destructively modifies its arguments. | |
207 var newData = mapValues(data, (each) => recursivelyFixUp(each, r, result)); | |
188 result[ruleNumber].add(newData); | 208 result[ruleNumber].add(newData); |
189 return new Reference(r, ruleNumber, result[ruleNumber].length - 1); | 209 return new Reference(r, ruleNumber, result[ruleNumber].length - 1); |
190 } | 210 } |
191 } | 211 } |
192 | 212 |
193 /** | 213 /** |
194 * Writes to a simple mostly-flat format. Details are subject to change. | 214 * Writes to a simple mostly-flat format. Details are subject to change. |
195 * Right now this produces a List containing null, num, and String. This is | 215 * Right now this produces a List containing null, num, and String. This is |
196 * more space-efficient than the map formats, but much less human-readable. | 216 * more space-efficient than the map formats, but much less human-readable. |
197 * Simple usage is to turn this into JSON for transmission. | 217 * Simple usage is to turn this into JSON for transmission. |
(...skipping 230 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
428 return new Reference(r, a, b); | 448 return new Reference(r, a, b); |
429 } | 449 } |
430 } | 450 } |
431 | 451 |
432 /** Return the next element from the input. */ | 452 /** Return the next element from the input. */ |
433 _next(Iterator input) { | 453 _next(Iterator input) { |
434 input.moveNext(); | 454 input.moveNext(); |
435 return input.current; | 455 return input.current; |
436 } | 456 } |
437 } | 457 } |
OLD | NEW |