| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 /** | |
| 6 * A general-purpose serialization facility for Dart objects. | |
| 7 * | |
| 8 * A [Serialization] is defined in terms of [SerializationRule]s and supports | |
| 9 * reading and writing to different formats. | |
| 10 * | |
| 11 * For information on installing and importing this library, see the | |
| 12 * [serialization package on pub.dartlang.org] | |
| 13 * (http://pub.dartlang.org/packages/serialization). | |
| 14 * | |
| 15 * ## Setup | |
| 16 * | |
| 17 * A simple example of usage is | |
| 18 * | |
| 19 * var address = new Address(); | |
| 20 * address.street = 'N 34th'; | |
| 21 * address.city = 'Seattle'; | |
| 22 * var serialization = new Serialization() | |
| 23 * ..addRuleFor(Address); | |
| 24 * Map output = serialization.write(address); | |
| 25 * | |
| 26 * This creates a new serialization and adds a rule for address objects. | |
| 27 * Then we ask the [Serialization] to write the address | |
| 28 * and we get back a Map which is a [json]able representation of the state of | |
| 29 * the address and related objects. Note that while the output in this case | |
| 30 * is a [Map], the type will vary depending on which output format we've told | |
| 31 * the [Serialization] to use. | |
| 32 * | |
| 33 * The version above used reflection to automatically identify the public | |
| 34 * fields of the address object. We can also specify those fields explicitly. | |
| 35 * | |
| 36 * var serialization = new Serialization() | |
| 37 * ..addRuleFor(Address, | |
| 38 * constructor: "create", | |
| 39 * constructorFields: ["number", "street"], | |
| 40 * fields: ["city"]); | |
| 41 * | |
| 42 * This rule still uses reflection to access the fields, but does not try to | |
| 43 * identify which fields to use, but instead uses only the "number" and "street" | |
| 44 * fields that we specified. We may also want to tell it to identify the | |
| 45 * fields, but to specifically omit certain fields that we don't want | |
| 46 * serialized. | |
| 47 * | |
| 48 * var serialization = new Serialization() | |
| 49 * ..addRuleFor(Address, | |
| 50 * constructor: "", | |
| 51 * excludeFields: ["other", "stuff"]); | |
| 52 * | |
| 53 * ## Writing rules | |
| 54 * | |
| 55 * We can also use a completely non-reflective rule to serialize and | |
| 56 * de-serialize objects. This can be more work, but it does work in | |
| 57 * dart2js, where mirrors are not yet implemented. We can specify this in two | |
| 58 * ways. First, we can write our own SerializationRule class that has methods | |
| 59 * for our Address class. | |
| 60 * | |
| 61 * class AddressRule extends CustomRule { | |
| 62 * bool appliesTo(instance, Writer w) => instance.runtimeType == Address; | |
| 63 * getState(instance) => [instance.street, instance.city]; | |
| 64 * create(state) => new Address(); | |
| 65 * setState(Address a, List state) { | |
| 66 * a.street = state[0]; | |
| 67 * a.city = state[1]; | |
| 68 * } | |
| 69 * } | |
| 70 * | |
| 71 * The class needs four different methods. The [CustomRule.appliesTo] | |
| 72 * method tells us if | |
| 73 * the rule should be used to write an object. In this example we use a test | |
| 74 * based on runtimeType. We could also use an "is Address" test, but if Address | |
| 75 * has subclasses that would find those as well, and we want a separate rule | |
| 76 * for each. The [CustomRule.getState] method should | |
| 77 * return all the state of the object that we want to recreate, | |
| 78 * and should be either a Map or a List. If you want to write to human-readable | |
| 79 * formats where it's useful to be able to look at the data as a map from | |
| 80 * field names to values, then it's better to return it as a map. Otherwise it's | |
| 81 * more efficient to return it as a list. You just need to be sure that the | |
| 82 * [CustomRule.create] and [CustomRule.setState] methods interpret the data the | |
| 83 * same way as [CustomRule.getState] does. | |
| 84 * | |
| 85 * The [CustomRule.create] method will create the new object and return it. Whil
e it's | |
| 86 * possible to create the object and set all its state in this one method, that | |
| 87 * increases the likelihood of problems with cycles. So it's better to use the | |
| 88 * minimum necessary information in [CustomRule.create] and do more of the work | |
| 89 * in [CustomRule.setState]. | |
| 90 * | |
| 91 * The other way to do this is not creating a subclass, but by using a | |
| 92 * [ClosureRule] and giving it functions for how to create | |
| 93 * the address. | |
| 94 * | |
| 95 * addressToMap(a) => {"number" : a.number, "street" : a.street, | |
| 96 * "city" : a.city}; | |
| 97 * createAddress(Map m) => new Address.create(m["number"], m["street"]); | |
| 98 * fillInAddress(Address a, Map m) => a.city = m["city"]; | |
| 99 * var serialization = new Serialization() | |
| 100 * ..addRule( | |
| 101 * new ClosureRule(anAddress.runtimeType, | |
| 102 * addressToMap, createAddress, fillInAddress); | |
| 103 * | |
| 104 * In this case we have created standalone functions rather than | |
| 105 * methods in a subclass and we pass them to the constructor of | |
| 106 * [ClosureRule]. In this case we've also had them use maps rather than | |
| 107 * lists for the state, but either would work as long as the rule is | |
| 108 * consistent with the representation it uses. We pass it the runtimeType | |
| 109 * of the object, and functions equivalent to the methods on [CustomRule] | |
| 110 * | |
| 111 * ## Constant values | |
| 112 * | |
| 113 * There are cases where the constructor needs values that we can't easily get | |
| 114 * from the serialized object. For example, we may just want to pass null, or a | |
| 115 * constant value. To support this, we can specify as constructor fields | |
| 116 * values that aren't field names. If any value isn't a String, or is a string | |
| 117 * that doesn't correspond to a field name, it will be | |
| 118 * treated as a constant and passed unaltered to the constructor. | |
| 119 * | |
| 120 * In some cases a non-constructor field should not be set using field | |
| 121 * access or a setter, but should be done by calling a method. For example, it | |
| 122 * may not be possible to set a List field "foo", and you need to call an | |
| 123 * addFoo() method for each entry in the list. In these cases, if you are using | |
| 124 * a BasicRule for the object you can call the setFieldWith() method. | |
| 125 * | |
| 126 * s..addRuleFor(FooHolder).setFieldWith("foo", | |
| 127 * (parent, value) => for (var each in value) parent.addFoo(value)); | |
| 128 * | |
| 129 * ## Writing | |
| 130 * | |
| 131 * To write objects, we use the write() method. | |
| 132 * | |
| 133 * var output = serialization.write(someObject); | |
| 134 * | |
| 135 * By default this uses a representation in which objects are represented as | |
| 136 * maps keyed by field name, but in which references between objects have been | |
| 137 * converted into Reference objects. This is then typically encoded as | |
| 138 * a [json] string, but can also be used in other ways, e.g. sent to another | |
| 139 * isolate. | |
| 140 * | |
| 141 * We can write objects in different formats by passing a [Format] object to | |
| 142 * the [Serialization.write] method or by getting a [Writer] object. | |
| 143 * The available formats | |
| 144 * include the default, a simple "flat" format that doesn't include field names, | |
| 145 * and a simple JSON format that produces output more suitable for talking to | |
| 146 * services that expect JSON in a predefined format. Examples of these are | |
| 147 * | |
| 148 * Map output = serialization.write(address, new SimpleMapFormat()); | |
| 149 * List output = serialization.write(address, new SimpleFlatFormat()); | |
| 150 * var output = serialization.write(address, new SimpleJsonFormat()); | |
| 151 * Or, using a [Writer] explicitly | |
| 152 * var writer = serialization.newWriter(new SimpleFlatFormat()); | |
| 153 * List output = writer.write(address); | |
| 154 * | |
| 155 * These representations are not yet considered stable. | |
| 156 * | |
| 157 * ## Reading | |
| 158 * | |
| 159 * To read objects, the corresponding [Serialization.read] method can be used. | |
| 160 * | |
| 161 * Address input = serialization.read(input); | |
| 162 * | |
| 163 * When reading, the serialization instance doing the reading must be configured | |
| 164 * with compatible rules to the one doing the writing. It's possible for the | |
| 165 * rules to be different, but they need to be able to read the same | |
| 166 * representation. For most practical purposes right now they should be the | |
| 167 * same. The simplest way to achieve this is by having the serialization | |
| 168 * variable [Serialization.selfDescribing] be true. In that case the rules | |
| 169 * themselves are also | |
| 170 * stored along with the serialized data, and can be read back on the receiving | |
| 171 * end. Note that this may not work for all rules or all formats. The | |
| 172 * [Serialization.selfDescribing] variable is true by default, but the | |
| 173 * [SimpleJsonFormat] does not support it, since the point is to provide a | |
| 174 * representation in a form | |
| 175 * other services might expect. Using CustomRule or ClosureRule also does not | |
| 176 * yet work with the [Serialization.selfDescribing] variable. | |
| 177 * | |
| 178 * ## Named objects | |
| 179 * | |
| 180 * When reading, some object references should not be serialized, but should be | |
| 181 * connected up to other instances on the receiving side. A notable example of | |
| 182 * this is when serialization rules have been stored. Instances of BasicRule | |
| 183 * take a [ClassMirror] in their constructor, and we cannot serialize those. So | |
| 184 * when we read the rules, we must provide a Map<String, Object> which maps from | |
| 185 * the simple name of classes we are interested in to a [ClassMirror]. This can | |
| 186 * be provided either in the [Serialization.namedObjects], | |
| 187 * or as an additional parameter to the reading and writing methods on the | |
| 188 * [Reader] or [Writer] respectively. | |
| 189 * | |
| 190 * new Serialization() | |
| 191 * ..addRuleFor(Person, constructorFields: ["name"]) | |
| 192 * ..namedObjects['Person'] = reflect(new Person()).type; | |
| 193 */ | |
| 194 library serialization; | |
| 195 | |
| 196 import 'src/mirrors_helpers.dart'; | |
| 197 import 'src/serialization_helpers.dart'; | |
| 198 import 'dart:collection'; | |
| 199 | |
| 200 part 'src/reader_writer.dart'; | |
| 201 part 'src/serialization_rule.dart'; | |
| 202 part 'src/basic_rule.dart'; | |
| 203 part 'src/format.dart'; | |
| 204 | |
| 205 /** | |
| 206 * This class defines a particular serialization scheme, in terms of | |
| 207 * [SerializationRule] instances, and supports reading and writing them. | |
| 208 * See library comment for examples of usage. | |
| 209 */ | |
| 210 class Serialization { | |
| 211 final List<SerializationRule> _rules; | |
| 212 | |
| 213 /** | |
| 214 * The serialization is controlled by the list of Serialization rules. These | |
| 215 * are most commonly added via [addRuleFor]. | |
| 216 */ | |
| 217 final UnmodifiableListView<SerializationRule> rules; | |
| 218 | |
| 219 /** | |
| 220 * When reading, we may need to resolve references to existing objects in | |
| 221 * the system. The right action may not be to create a new instance of | |
| 222 * something, but rather to find an existing instance and connect to it. | |
| 223 * For example, if we have are serializing an Email message and it has a | |
| 224 * link to the owning account, it may not be appropriate to try and serialize | |
| 225 * the account. Instead we should just connect the de-serialized message | |
| 226 * object to the account object that already exists there. | |
| 227 */ | |
| 228 Map<String, dynamic> namedObjects = {}; | |
| 229 | |
| 230 /** | |
| 231 * When we write out data using this serialization, should we also write | |
| 232 * out a description of the rules. This is on by default unless using | |
| 233 * CustomRule subclasses, in which case it requires additional setup and | |
| 234 * is off by default. | |
| 235 */ | |
| 236 bool _selfDescribing; | |
| 237 | |
| 238 /** | |
| 239 * When we write out data using this serialization, should we also write | |
| 240 * out a description of the rules. This is on by default unless using | |
| 241 * CustomRule subclasses, in which case it requires additional setup and | |
| 242 * is off by default. | |
| 243 */ | |
| 244 bool get selfDescribing { | |
| 245 // TODO(alanknight): Should this be moved to the format? | |
| 246 // TODO(alanknight): Allow self-describing in the presence of CustomRule. | |
| 247 // TODO(alanknight): Don't do duplicate work creating a writer and | |
| 248 // serialization here and then re-creating when we actually serialize. | |
| 249 if (_selfDescribing != null) return _selfDescribing; | |
| 250 var meta = ruleSerialization(); | |
| 251 var w = meta.newWriter(); | |
| 252 _selfDescribing = !rules.any((rule) => | |
| 253 meta.rulesFor(rule, w, create: false).isEmpty); | |
| 254 return _selfDescribing; | |
| 255 } | |
| 256 | |
| 257 /** | |
| 258 * When we write out data using this serialization, should we also write | |
| 259 * out a description of the rules. This is on by default unless using | |
| 260 * CustomRule subclasses, in which case it requires additional setup and | |
| 261 * is off by default. | |
| 262 */ | |
| 263 void set selfDescribing(bool value) { | |
| 264 _selfDescribing = value; | |
| 265 } | |
| 266 | |
| 267 /** | |
| 268 * Creates a new serialization with a default set of rules for primitives | |
| 269 * and lists. | |
| 270 */ | |
| 271 factory Serialization() => | |
| 272 new Serialization.blank() | |
| 273 ..addDefaultRules(); | |
| 274 | |
| 275 /** | |
| 276 * Creates a new serialization with no default rules at all. The most common | |
| 277 * use for this is if we are reading self-describing serialized data and | |
| 278 * will populate the rules from that data. | |
| 279 */ | |
| 280 factory Serialization.blank() | |
| 281 => new Serialization._(new List<SerializationRule>()); | |
| 282 | |
| 283 Serialization._(List<SerializationRule> rules) : | |
| 284 this._rules = rules, | |
| 285 this.rules = new UnmodifiableListView(rules); | |
| 286 | |
| 287 /** | |
| 288 * Create a [BasicRule] rule for [instanceOrType]. Normally this will be | |
| 289 * a type, but for backward compatibilty we also allow you to pass an | |
| 290 * instance (except an instance of Type), and the rule will be created | |
| 291 * for its runtimeType. Optionally | |
| 292 * allows specifying a [constructor] name, the list of [constructorFields], | |
| 293 * and the list of [fields] not used in the constructor. Returns the new | |
| 294 * rule. Note that [BasicRule] uses reflection, and so will not work with the | |
| 295 * current state of dartj2s. If you need to run there, consider using | |
| 296 * [CustomRule] instead. | |
| 297 * | |
| 298 * If the optional parameters aren't specified, the default constructor will | |
| 299 * be used, and the list of fields will be computed. Alternatively, you can | |
| 300 * omit [fields] and provide [excludeFields], which will then compute the | |
| 301 * list of fields specifically excluding those listed. | |
| 302 * | |
| 303 * The fields can be actual public fields, but can also be getter/setter | |
| 304 * pairs or getters whose value is provided in the constructor. For the | |
| 305 * [constructorFields] they can also be arbitrary objects. Anything that is | |
| 306 * not a String will be treated as a constant value to be used in any | |
| 307 * construction of these objects. | |
| 308 * | |
| 309 * If the list of fields is computed, fields from the superclass will be | |
| 310 * included. However, each subclass needs its own rule, since the constructors | |
| 311 * are not inherited, and so may need to be specified separately for each | |
| 312 * subclass. | |
| 313 */ | |
| 314 BasicRule addRuleFor( | |
| 315 instanceOrType, | |
| 316 {String constructor, | |
| 317 List constructorFields, | |
| 318 List<String> fields, | |
| 319 List<String> excludeFields}) { | |
| 320 | |
| 321 var rule = new BasicRule( | |
| 322 turnInstanceIntoSomethingWeCanUse( | |
| 323 instanceOrType), | |
| 324 constructor, constructorFields, fields, excludeFields); | |
| 325 addRule(rule); | |
| 326 return rule; | |
| 327 } | |
| 328 | |
| 329 /** Set up the default rules, for lists and primitives. */ | |
| 330 void addDefaultRules() { | |
| 331 addRule(new PrimitiveRule()); | |
| 332 addRule(new ListRule()); | |
| 333 // Both these rules apply to lists, so unless otherwise indicated, | |
| 334 // it will always find the first one. | |
| 335 addRule(new ListRuleEssential()); | |
| 336 addRule(new MapRule()); | |
| 337 addRule(new SymbolRule()); | |
| 338 addRule(new DateTimeRule()); | |
| 339 } | |
| 340 | |
| 341 /** | |
| 342 * Add a new SerializationRule [rule]. The addRuleFor method will probably | |
| 343 * handle most simple cases, but for adding an arbitrary rule, including | |
| 344 * a SerializationRule subclass which you have created, you can use this | |
| 345 * method. | |
| 346 */ | |
| 347 void addRule(SerializationRule rule) { | |
| 348 rule.number = _rules.length; | |
| 349 _rules.add(rule); | |
| 350 } | |
| 351 | |
| 352 /** | |
| 353 * This writes out an object graph rooted at [object] and returns the result. | |
| 354 * The [format] parameter determines the form of the result. The default | |
| 355 * format is [SimpleMapFormat] and returns a Map. | |
| 356 */ | |
| 357 write(Object object, {Format format}) { | |
| 358 return newWriter(format).write(object); | |
| 359 } | |
| 360 | |
| 361 /** | |
| 362 * Return a new [Writer] object for this serialization. This is useful if you | |
| 363 * want to do something more complex with the writer than just returning | |
| 364 * the final result. | |
| 365 */ | |
| 366 Writer newWriter([Format format]) => new Writer(this, format); | |
| 367 | |
| 368 /** | |
| 369 * Read the serialized data from [input] and return the root object | |
| 370 * from the result. The [input] can be of any type that the [Format] | |
| 371 * reads/writes, but normally will be a [List], [Map], or a simple type. | |
| 372 * The [format] parameter determines the form of the result. The default | |
| 373 * format is [SimpleMapFormat] and expects a Map as input. | |
| 374 * If there are objects that need to be resolved | |
| 375 * in the current context, they should be provided in [externals] as a | |
| 376 * Map from names to values. In particular, in the current implementation | |
| 377 * any class mirrors needed should be provided in [externals] using the | |
| 378 * class name as a key. In addition to the [externals] map provided here, | |
| 379 * values will be looked up in the [namedObjects] map. | |
| 380 */ | |
| 381 read(input, {Format format, Map externals: const {}}) { | |
| 382 return newReader(format).read(input, externals); | |
| 383 } | |
| 384 | |
| 385 /** | |
| 386 * Return a new [Reader] object for this serialization. This is useful if | |
| 387 * you want to do something more complex with the reader than just returning | |
| 388 * the final result. | |
| 389 */ | |
| 390 Reader newReader([Format format]) => new Reader(this, format); | |
| 391 | |
| 392 /** | |
| 393 * Return the list of SerializationRule that apply to [object]. For | |
| 394 * internal use, but public because it's used in testing. | |
| 395 */ | |
| 396 Iterable<SerializationRule> rulesFor(object, Writer w, {create : true}) { | |
| 397 // This has a couple of edge cases. | |
| 398 // 1) The owning object may have indicated we should use a different | |
| 399 // rule than the default. | |
| 400 // 2) We may not have a rule, in which case we lazily create a BasicRule. | |
| 401 // 3) Rules are allowed to say mustBePrimary, meaning that they can be used | |
| 402 // iff no other rule was chosen first. | |
| 403 // TODO(alanknight): Can the mustBePrimary mechanism be removed or changed. | |
| 404 // It adds an order dependency to the rules, and is messy. Reconsider in the | |
| 405 // light of a more general mechanism for multiple rules per object. | |
| 406 // TODO(alanknight): Finding which rules apply seems likely to be a | |
| 407 // bottleneck, particularly with the current reflective implementation. | |
| 408 // Consider how to improve it. e.g. cache the list of rules by class. But | |
| 409 // be careful of issues like rules which have arbitrary predicates. Or | |
| 410 // consider having the arbitrary predicates be secondary to an initial | |
| 411 // class-based lookup mechanism. | |
| 412 var target, candidateRules; | |
| 413 if (object is DesignatedRuleForObject) { | |
| 414 target = object.target; | |
| 415 candidateRules = object.possibleRules(rules); | |
| 416 } else { | |
| 417 target = object; | |
| 418 candidateRules = rules; | |
| 419 } | |
| 420 Iterable applicable = candidateRules.where( | |
| 421 (each) => each.appliesTo(target, w)); | |
| 422 | |
| 423 if (applicable.isEmpty) { | |
| 424 return create ? [addRuleFor(target)] : applicable; | |
| 425 } | |
| 426 | |
| 427 if (applicable.length == 1) return applicable; | |
| 428 var first = applicable.first; | |
| 429 var finalRules = applicable.where( | |
| 430 (x) => !x.mustBePrimary || (x == first)); | |
| 431 | |
| 432 if (finalRules.isEmpty) throw new SerializationException( | |
| 433 'No valid rule found for object $object'); | |
| 434 return finalRules; | |
| 435 } | |
| 436 | |
| 437 /** | |
| 438 * Create a Serialization for serializing SerializationRules. This is used | |
| 439 * to save the rules in a self-describing format along with the data. | |
| 440 * If there are new rule classes created, they will need to be described | |
| 441 * here. | |
| 442 */ | |
| 443 Serialization ruleSerialization() { | |
| 444 // TODO(alanknight): There's an extensibility issue here with new rules. | |
| 445 // TODO(alanknight): How to handle rules with closures? They have to | |
| 446 // exist on the other side, but we might be able to hook them up by name, | |
| 447 // or we might just be able to validate that they're correctly set up | |
| 448 // on the other side. | |
| 449 | |
| 450 var meta = new Serialization() | |
| 451 ..selfDescribing = false | |
| 452 ..addRuleFor(ListRule) | |
| 453 ..addRuleFor(MapRule) | |
| 454 ..addRuleFor(PrimitiveRule) | |
| 455 ..addRuleFor(ListRuleEssential) | |
| 456 ..addRuleFor(BasicRule, | |
| 457 constructorFields: ['type', | |
| 458 'constructorName', | |
| 459 'constructorFields', 'regularFields', []], | |
| 460 fields: []) | |
| 461 ..addRule(new NamedObjectRule()) | |
| 462 ..addRule(new MirrorRule()) | |
| 463 ..addRuleFor(MirrorRule) | |
| 464 ..addRuleFor(SymbolRule) | |
| 465 ..addRuleFor(DateTimeRule); | |
| 466 meta.namedObjects = namedObjects; | |
| 467 return meta; | |
| 468 } | |
| 469 | |
| 470 /** Return true if our [namedObjects] collection has an entry for [object].*/ | |
| 471 bool _hasNameFor(object) { | |
| 472 var sentinel = const _Sentinel(); | |
| 473 return _nameFor(object, () => sentinel) != sentinel; | |
| 474 } | |
| 475 | |
| 476 /** | |
| 477 * Return the name we have for [object] in our [namedObjects] collection or | |
| 478 * the result of evaluating [ifAbsent] if there is no entry. | |
| 479 */ | |
| 480 _nameFor(object, [ifAbsent]) { | |
| 481 for (var key in namedObjects.keys) { | |
| 482 if (identical(namedObjects[key], object)) return key; | |
| 483 } | |
| 484 return ifAbsent == null ? null : ifAbsent(); | |
| 485 } | |
| 486 } | |
| 487 | |
| 488 /** | |
| 489 * An exception class for errors during serialization. | |
| 490 */ | |
| 491 class SerializationException implements Exception { | |
| 492 final String message; | |
| 493 const SerializationException(this.message); | |
| 494 String toString() => "SerializationException($message)"; | |
| 495 } | |
| OLD | NEW |