OLD | NEW |
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 * This provides a general-purpose serialization facility for Dart objects. A | 6 * This provides a general-purpose serialization facility for Dart objects. A |
7 * [Serialization] is defined in terms of [SerializationRule]s and supports | 7 * [Serialization] is defined in terms of [SerializationRule]s and supports |
8 * reading and writing to different formats. | 8 * reading and writing to different formats. |
9 * | 9 * |
10 * Setup | 10 * Setup |
11 * ===== | 11 * ===== |
12 * A simple example of usage is | 12 * A simple example of usage is |
13 * | 13 * |
14 * var address = new Address(); | 14 * var address = new Address(); |
15 * address.street = 'N 34th'; | 15 * address.street = 'N 34th'; |
16 * address.city = 'Seattle'; | 16 * address.city = 'Seattle'; |
17 * var serialization = new Serialization() | 17 * var serialization = new Serialization() |
18 * ..addRuleFor(address); | 18 * ..addRuleFor(address); |
19 * String output = serialization.write(address); | 19 * String output = serialization.write(address); |
20 * | 20 * |
21 * This creates a new serialization and adds a rule for address objects. Right | 21 * This creates a new serialization and adds a rule for address objects. Right |
22 * now it has to be passed an address instance because of limitations using | 22 * now it has to be passed an address instance because of limitations using |
23 * Address as a literal. Then we ask the Serialization to write the address | 23 * Address as a literal. Then we ask the Serialization to write the address |
24 * and we get back a String which is a [JSON] representation of the state of | 24 * and we get back a Map which is a [json]able representation of the state of |
25 * it and related objects. | 25 * the address and related objects. |
26 * | 26 * |
27 * The version above used reflection to automatically identify the public | 27 * The version above used reflection to automatically identify the public |
28 * fields of the address object. We can also specify those fields explicitly. | 28 * fields of the address object. We can also specify those fields explicitly. |
29 * | 29 * |
30 * var serialization = new Serialization() | 30 * var serialization = new Serialization() |
31 * ..addRuleFor(address, | 31 * ..addRuleFor(address, |
32 * constructor: "create", | 32 * constructor: "create", |
33 * constructorFields: ["number", "street"], | 33 * constructorFields: ["number", "street"], |
34 * fields: ["city"]); | 34 * fields: ["city"]); |
35 * | 35 * |
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
98 * [ClosureRule]. In this case we've also had them use maps rather than | 98 * [ClosureRule]. In this case we've also had them use maps rather than |
99 * lists for the state, but either would work as long as the rule is | 99 * lists for the state, but either would work as long as the rule is |
100 * consistent with the representation it uses. We pass it the runtimeType | 100 * consistent with the representation it uses. We pass it the runtimeType |
101 * of the object, and functions equivalent to the methods on [CustomRule] | 101 * of the object, and functions equivalent to the methods on [CustomRule] |
102 * | 102 * |
103 * Constant Values | 103 * Constant Values |
104 * =============== | 104 * =============== |
105 * There are cases where the constructor needs values that we can't easily get | 105 * There are cases where the constructor needs values that we can't easily get |
106 * from the serialized object. For example, we may just want to pass null, or a | 106 * from the serialized object. For example, we may just want to pass null, or a |
107 * constant value. To support this, we can specify as constructor fields | 107 * constant value. To support this, we can specify as constructor fields |
108 * values that aren't field names. If any value isn't a String, it will be | 108 * values that aren't field names. If any value isn't a String, or is a string |
| 109 * that doesn't correspond to a field name, it will be |
109 * treated as a constant and passed unaltered to the constructor. | 110 * treated as a constant and passed unaltered to the constructor. |
110 * | 111 * |
111 * In some cases a non-constructor field should not be set using field | 112 * In some cases a non-constructor field should not be set using field |
112 * access or a setter, but should be done by calling a method. For example, it | 113 * access or a setter, but should be done by calling a method. For example, it |
113 * may not be possible to set a List field "foo", and you need to call an | 114 * may not be possible to set a List field "foo", and you need to call an |
114 * addFoo() method for each entry in the list. In these cases, if you are using | 115 * addFoo() method for each entry in the list. In these cases, if you are using |
115 * a BasicRule for the object you can call the setFieldWith() method. | 116 * a BasicRule for the object you can call the setFieldWith() method. |
116 * | 117 * |
117 * s..addRuleFor(fooHolderInstance).setFieldWith("foo", | 118 * s..addRuleFor(fooHolderInstance).setFieldWith("foo", |
118 * (parent, value) => for (var each in value) parent.addFoo(value)); | 119 * (parent, value) => for (var each in value) parent.addFoo(value)); |
119 * | 120 * |
120 * Writing | 121 * Writing |
121 * ======= | 122 * ======= |
122 * To write objects, we use the write() method. | 123 * To write objects, we use the write() method. |
123 * | 124 * |
124 * var output = serialization.write(someObject); | 125 * var output = serialization.write(someObject); |
125 * | 126 * |
126 * By default this uses a representation in which objects are represented as | 127 * By default this uses a representation in which objects are represented as |
127 * maps keyed by field name, but in which references between objects have been | 128 * maps keyed by field name, but in which references between objects have been |
128 * converted into Reference objects. This is then encoded as a [json] string. | 129 * converted into Reference objects. This is then typically encoded as |
| 130 * a [json] string, but can also be used in other ways, e.g. sent to another |
| 131 * isolate. |
129 * | 132 * |
130 * We can write objects in different formats by passing a [Format] object to | 133 * We can write objects in different formats by passing a [Format] object to |
131 * the [write] method or by getting a [Writer] object. The available formats | 134 * the [write] method or by getting a [Writer] object. The available formats |
132 * include the default, a simple "flat" format that doesn't include field names, | 135 * include the default, a simple "flat" format that doesn't include field names, |
133 * and a simple JSON format that produces output more suitable for talking to | 136 * and a simple JSON format that produces output more suitable for talking to |
134 * services that expect JSON in a predefined format. Examples of these are | 137 * services that expect JSON in a predefined format. Examples of these are |
135 * | 138 * |
136 * String output = serialization.write(address, new SimpleMapFormat()); | 139 * Map output = serialization.write(address, new SimpleMapFormat()); |
137 * List output = serialization.write(address, new SimpleFlatFormat()); | 140 * List output = serialization.write(address, new SimpleFlatFormat()); |
138 * Map output = serialization.write(address, new SimpleJsonFormat()); | 141 * var output = serialization.write(address, new SimpleJsonFormat()); |
139 * Or, using a [Writer] explicitly | 142 * Or, using a [Writer] explicitly |
140 * var writer = serialization.newWriter(new SimpleFlatFormat()); | 143 * var writer = serialization.newWriter(new SimpleFlatFormat()); |
141 * var output = writer.write(address); | 144 * List output = writer.write(address); |
142 * | 145 * |
143 * These representations are not yet considered stable. | 146 * These representations are not yet considered stable. |
144 * | 147 * |
145 * Reading | 148 * Reading |
146 * ======= | 149 * ======= |
147 * To read objects, the corresponding [read] method can be used. | 150 * To read objects, the corresponding [read] method can be used. |
148 * | 151 * |
149 * Address input = serialization.read(aString); | 152 * Address input = serialization.read(input); |
150 * | 153 * |
151 * When reading, the serialization instance doing the reading must be configured | 154 * When reading, the serialization instance doing the reading must be configured |
152 * with compatible rules to the one doing the writing. It's possible for the | 155 * with compatible rules to the one doing the writing. It's possible for the |
153 * rules to be different, but they need to be able to read the same | 156 * rules to be different, but they need to be able to read the same |
154 * representation. For most practical purposes right now they should be the | 157 * representation. For most practical purposes right now they should be the |
155 * same. The simplest way to achieve this is by having the serialization | 158 * same. The simplest way to achieve this is by having the serialization |
156 * variable [selfDescribing] be true. In that case the rules themselves are also | 159 * variable [selfDescribing] be true. In that case the rules themselves are also |
157 * stored along with the serialized data, and can be read back on the receiving | 160 * stored along with the serialized data, and can be read back on the receiving |
158 * end. Note that this may not work for all rules or all formats. The | 161 * end. Note that this may not work for all rules or all formats. The |
159 * [selfDescribing] variable is true by default, but the [SimpleJsonFormat] does | 162 * [selfDescribing] variable is true by default, but the [SimpleJsonFormat] does |
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
228 */ | 231 */ |
229 bool _selfDescribing; | 232 bool _selfDescribing; |
230 | 233 |
231 /** | 234 /** |
232 * When we write out data using this serialization, should we also write | 235 * When we write out data using this serialization, should we also write |
233 * out a description of the rules. This is on by default unless using | 236 * out a description of the rules. This is on by default unless using |
234 * CustomRule subclasses, in which case it requires additional setup and | 237 * CustomRule subclasses, in which case it requires additional setup and |
235 * is off by default. | 238 * is off by default. |
236 */ | 239 */ |
237 bool get selfDescribing { | 240 bool get selfDescribing { |
| 241 // TODO(alanknight): Should this be moved to the format? |
| 242 // TODO(alanknight): Allow self-describing in the presence of CustomRule. |
238 if (_selfDescribing != null) return _selfDescribing; | 243 if (_selfDescribing != null) return _selfDescribing; |
239 return !_rules.any((x) => x is CustomRule); | 244 return !_rules.any((x) => x is CustomRule); |
240 } | 245 } |
241 | 246 |
242 /** | 247 /** |
243 * When we write out data using this serialization, should we also write | 248 * When we write out data using this serialization, should we also write |
244 * out a description of the rules. This is on by default unless using | 249 * out a description of the rules. This is on by default unless using |
245 * CustomRule subclasses, in which case it requires additional setup and | 250 * CustomRule subclasses, in which case it requires additional setup and |
246 * is off by default. | 251 * is off by default. |
247 */ | 252 */ |
(...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
337 /** | 342 /** |
338 * Return a new [Writer] object for this serialization. This is useful if you | 343 * Return a new [Writer] object for this serialization. This is useful if you |
339 * want to do something more complex with the writer than just returning | 344 * want to do something more complex with the writer than just returning |
340 * the final result. | 345 * the final result. |
341 */ | 346 */ |
342 Writer newWriter([Format format]) => | 347 Writer newWriter([Format format]) => |
343 new Writer(this, format); | 348 new Writer(this, format); |
344 | 349 |
345 /** | 350 /** |
346 * Read the serialized data from [input] and return the root object | 351 * Read the serialized data from [input] and return the root object |
347 * from the result. If there are objects that need to be resolved | 352 * from the result. The [input] can be of any type that the [Format] |
| 353 * reads/writes, but normally will be a [List], [Map], or a simple type. |
| 354 * If there are objects that need to be resolved |
348 * in the current context, they should be provided in [externals] as a | 355 * in the current context, they should be provided in [externals] as a |
349 * Map from names to values. In particular, in the current implementation | 356 * Map from names to values. In particular, in the current implementation |
350 * any class mirrors needed should be provided in [externals] using the | 357 * any class mirrors needed should be provided in [externals] using the |
351 * class name as a key. In addition to the [externals] map provided here, | 358 * class name as a key. In addition to the [externals] map provided here, |
352 * values will be looked up in the [externalObjects] map. | 359 * values will be looked up in the [namedObjects] map. |
353 */ | 360 */ |
354 read(String input, [Map externals = const {}]) { | 361 read(input, [Map externals = const {}]) { |
355 return newReader().read(input, externals); | 362 return newReader().read(input, externals); |
356 } | 363 } |
357 | 364 |
358 /** | 365 /** |
359 * Return a new [Reader] object for this serialization. This is useful if | 366 * Return a new [Reader] object for this serialization. This is useful if |
360 * you want to do something more complex with the reader than just returning | 367 * you want to do something more complex with the reader than just returning |
361 * the final result. | 368 * the final result. |
362 */ | 369 */ |
363 Reader newReader([Format format]) => new Reader(this, format); | 370 Reader newReader([Format format]) => new Reader(this, format); |
364 | 371 |
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
460 } | 467 } |
461 | 468 |
462 /** | 469 /** |
463 * An exception class for errors during serialization. | 470 * An exception class for errors during serialization. |
464 */ | 471 */ |
465 class SerializationException implements Exception { | 472 class SerializationException implements Exception { |
466 final String message; | 473 final String message; |
467 const SerializationException([this.message]); | 474 const SerializationException([this.message]); |
468 toString() => "SerializationException($message)"; | 475 toString() => "SerializationException($message)"; |
469 } | 476 } |
OLD | NEW |