| 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 * This provides a general-purpose serialization facility for Dart objects. A |
| 7 * [Serialization] is defined in terms of [SerializationRule]s and supports |
| 8 * reading and writing to different formats. |
| 9 * |
| 10 * Setup |
| 11 * ===== |
| 12 * A simple example of usage is |
| 13 * |
| 14 * var address = new Address(); |
| 15 * address.street = 'N 34th'; |
| 16 * address.city = 'Seattle'; |
| 17 * var serialization = new Serialization() |
| 18 * ..addRuleFor(address); |
| 19 * String output = serialization.write(address); |
| 20 * |
| 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 we can't write Address |
| 23 * as a literal. Then we ask the Serialization to write the address and we get |
| 24 * back a String which is a [JSON] representation of the state of it and related |
| 25 * objects. |
| 26 * |
| 27 * The version above used reflection to automatically identify the public |
| 28 * fields of the address object. We can also specify those fields explicitly. |
| 29 * |
| 30 * var serialization = new Serialization() |
| 31 * ..addRuleFor(address, |
| 32 * constructor: "create", |
| 33 * constructorFields: ["number", "street"], |
| 34 * fields: ["city"]); |
| 35 * |
| 36 * This rule still uses reflection to access the fields, but does not try to |
| 37 * identify which fields to use, but instead uses only the "number" and "street" |
| 38 * fields that we specified. We may also want to tell it to identify the |
| 39 * fields, but to specifically omit certain fields that we don't want |
| 40 * serialized. |
| 41 * |
| 42 * var serialization = new Serialization() |
| 43 * ..addRuleFor(address, |
| 44 * constructor: "", |
| 45 * excludeFields: ["other", "stuff"]); |
| 46 * |
| 47 * We can also use a completely non-reflective rule to serialize and |
| 48 * de-serialize objects. |
| 49 * |
| 50 * addressToMap(a) => {"number" : a.number, "street" : a.street, |
| 51 * "city" : a.city}; |
| 52 * createAddress(Map m) => new Address.create(m["number"], m["street"]); |
| 53 * fillInAddress(Map m, Address a) => a.city = m["city"]; |
| 54 * var serialization = new Serialization() |
| 55 * ..addRule( |
| 56 * new ClosureToMapRule(anAddress.runtimeType, |
| 57 * addressToMap, createAddress, fillInAddress); |
| 58 * |
| 59 * Note that there are three different functions provided. The addressToMap |
| 60 * function takes the fields we want serialized from the Address and puts them |
| 61 * into a map. The createAddress function creates a new address using a map like |
| 62 * the one returned by the first function. And the fillInAddress function fills |
| 63 * in any remaining state in the created object. |
| 64 * |
| 65 * At the moment, however, using this rule increases the probability of problems |
| 66 * with cycles. The problem is that before passing values to the user-supplied |
| 67 * functions it has to inflate any references to be the real objects. Since it |
| 68 * doesn't know which ones the creation function uses it has to inflate all of |
| 69 * them. For example, consider a Node class with parent, leftChild, and |
| 70 * rightChild, and the parent field was final and set in the constructor. When |
| 71 * we inflate all of the values we will end up with a cycle and can't |
| 72 * de-serialize. If we know which fields are used by the constructor we can |
| 73 * inflate only those, which is what BasicRule does. We expect to make a richer |
| 74 * API for rules not using reflection, but there's a tension between providing |
| 75 * the serialization process with enough information and making it more work |
| 76 * to specify. |
| 77 * |
| 78 * There are cases where the constructor needs values that we can't easily get |
| 79 * the serialized object. For example, we may just want to pass null, or a |
| 80 * constant value. To support this, we can specify as constructor fields |
| 81 * values that aren't field names. If any value isn't a String, it will be |
| 82 * treated as a constant and passed unaltered to the constructor. |
| 83 * |
| 84 * In some cases a non-constructor field should not be set using field |
| 85 * access or a setter, but should be done by calling a method. For example, it |
| 86 * may not be possible to set a List field "foo", and you need to call an |
| 87 * addFoo() method for each entry in the list. In these cases, if you are using |
| 88 * a BasicRule for the object you can call the specialTreatmentFor() method. |
| 89 * |
| 90 * s..addRuleFor(fooHolderInstance).specialTreatmentFor("foo", |
| 91 * (parent, value) => for (var each in value) parent.addFoo(value)); |
| 92 * |
| 93 * Writing |
| 94 * ======= |
| 95 * To write objects, we use the write() methods. There are two variations. |
| 96 * |
| 97 * String output = serialization.write(someObject); |
| 98 * List output = serialization.writeFlat(someObject); |
| 99 * |
| 100 * The first uses a representation in which objects are represented as maps |
| 101 * keyed by field name, but in which references between objects have been |
| 102 * converted into Reference objects. This is then encoded as a [JSON] string. |
| 103 * |
| 104 * The second representation holds all the objects as a List of simple types. |
| 105 * For practical use you may want to convert that to a [JSON] or other encoded |
| 106 * representation as well. |
| 107 * |
| 108 * Both representations are primarily intended as proofs of concept for |
| 109 * different types of representation, and we expect to generalize that to a |
| 110 * pluggable mechanism for different representations. |
| 111 * |
| 112 * Reading |
| 113 * ======= |
| 114 * To read objects, the corresponding methods are [read] and [readFlat]. |
| 115 * |
| 116 * List input = serialization.read(aString); |
| 117 * List input = serialization.readFlat(aList); |
| 118 * |
| 119 * There is also a convenience method for the case of reading a single object. |
| 120 * |
| 121 * Object result = serialization.readOne(aString); |
| 122 * Object result = serialization.readOneFlat(aString); |
| 123 * |
| 124 * When reading, the serialization instance doing the reading must be configured |
| 125 * with compatible rules to the one doing the writing. It's possible for the |
| 126 * rules to be different, but they need to be able to read the same |
| 127 * representation. For most practical purposes right now they should be the |
| 128 * same. The simplest way to achieve this is by having the serialization |
| 129 * variable [selfDescribing] be true. In that case the rules themselves are also |
| 130 * stored along with the serialized data, and can be read back on the receiving |
| 131 * end. Note that this does not yet work for [ClosureToMapRule]. The |
| 132 * [selfDescribing] variable is true by default. |
| 133 * |
| 134 * When reading, some object references should not be serialized, but should be |
| 135 * connected up to other instances on the receiving side. A notable example of |
| 136 * this is when serialization rules have been stored. Instances of BasicRule |
| 137 * take a [ClassMirror] in their constructor, and we cannot serialize those. So |
| 138 * when we read the rules, we must provide a Map<String, Object> which maps from |
| 139 * the simple name of classes we are interested in to a [ClassMirror]. This can |
| 140 * be provided either in the [externalObjects] variable of the Serialization, |
| 141 * or as an additional parameter to the reading methods. |
| 142 * |
| 143 * new Serialization() |
| 144 * ..addRuleFor(new Person(), constructorFields: ["name"]) |
| 145 * ..externalObjects['Person'] = reflect(new Person()).type; |
| 146 */ |
| 147 library serialization; |
| 148 |
| 149 import 'src/mirrors_helpers.dart'; |
| 150 import 'src/serialization_helpers.dart'; |
| 151 import 'src/polyfill_identity_set.dart'; |
| 152 import 'dart:json' show JSON; |
| 153 |
| 154 part 'src/reader_writer.dart'; |
| 155 part 'src/serialization_rule.dart'; |
| 156 part 'src/basic_rule.dart'; |
| 157 |
| 158 /** |
| 159 * This class defines a particular serialization scheme, in terms of |
| 160 * [SerializationRule] instances, and supports reading and writing them. |
| 161 * See library comment for examples of usage. |
| 162 */ |
| 163 class Serialization { |
| 164 |
| 165 /** |
| 166 * The serialization is controlled by the list of Serialization rules. These |
| 167 * are most commonly added via [addRuleFor]. |
| 168 */ |
| 169 List rules = []; |
| 170 |
| 171 /** |
| 172 * When reading, we may need to resolve references to existing objects in |
| 173 * the system. The right action may not be to create a new instance of |
| 174 * something, but rather to find an existing instance and connect to it. |
| 175 * For example, if we have are serializing an Email message and it has a |
| 176 * link to the owning account, it may not be appropriate to try and serialize |
| 177 * the account. Instead we should just connect the de-serialized message |
| 178 * object to the account object that already exists there. |
| 179 */ |
| 180 Map<String, dynamic> externalObjects = {}; |
| 181 |
| 182 /** |
| 183 * When we write out data using this serialization, should we also write |
| 184 * out a description of the rules. |
| 185 */ |
| 186 bool selfDescribing = true; |
| 187 |
| 188 /** |
| 189 * Creates a new serialization with a default set of rules for primitives |
| 190 * and lists. |
| 191 */ |
| 192 Serialization() { |
| 193 addDefaultRules(); |
| 194 } |
| 195 |
| 196 /** |
| 197 * Creates a new serialization with no default rules at all. The most common |
| 198 * use for this is if we are reading self-describing serialized data and |
| 199 * will populate the rules from that data. |
| 200 */ |
| 201 Serialization.blank() { } |
| 202 |
| 203 /** |
| 204 * Create a [BasicRule] rule for the type of |
| 205 * [instanceOfType]. Optionally |
| 206 * allows specifying a [constructor] name, the list of [constructorFields], |
| 207 * and the list of [fields] not used in the constructor. Returns the new |
| 208 * rule. |
| 209 * |
| 210 * If the optional parameters aren't specified, the default constructor will |
| 211 * be used, and the list of fields will be computed. Alternatively, you can |
| 212 * omit [fields] and provide [excludeFields], which will then compute the |
| 213 * list of fields specifically excluding those listed. |
| 214 * |
| 215 * The fields can be actual public fields, but can also be getter/setter |
| 216 * pairs or getters whose value is provided in the constructor. For the |
| 217 * [constructorFields] they can also be arbitrary objects. Anything that is |
| 218 * not a String will be treated as a constant value to be used in any |
| 219 * construction of these objects. |
| 220 * |
| 221 * If the list of fields is computed, fields from the superclass will be |
| 222 * included. However, each subclass needs its own rule, since the constructors |
| 223 * are not inherited, and so may need to be specified separately for each |
| 224 * subclass. |
| 225 */ |
| 226 // TODO(alanknight): Take a type rather than an instance. Issue 6282 and 6433. |
| 227 BasicRule addRuleFor( |
| 228 instanceOfType, |
| 229 {String constructor, |
| 230 List constructorFields, |
| 231 List<String> fields, |
| 232 List<String> excludeFields}) { |
| 233 |
| 234 var rule; |
| 235 rule = new BasicRule( |
| 236 turnInstanceIntoSomethingWeCanUse( |
| 237 instanceOfType), |
| 238 constructor, constructorFields, fields, excludeFields); |
| 239 addRule(rule); |
| 240 return rule; |
| 241 } |
| 242 |
| 243 /** Set up the default rules, for lists and primitives. */ |
| 244 void addDefaultRules() { |
| 245 addRule(new PrimitiveRule()); |
| 246 addRule(new ListRule()); |
| 247 // Both these rules apply to lists, so unless otherwise indicated, |
| 248 // it will always find the first one. |
| 249 addRule(new ListRuleEssential()); |
| 250 } |
| 251 |
| 252 /** |
| 253 * Add a new SerializationRule [rule]. The addRuleFor method will probably |
| 254 * handle most simple cases, but for adding an arbitrary rule, including |
| 255 * a SerializationRule subclass which you have created, you can use this |
| 256 * method. |
| 257 */ |
| 258 void addRule(SerializationRule rule) { |
| 259 rule.number = rules.length; |
| 260 rules.add(rule); |
| 261 } |
| 262 |
| 263 /** |
| 264 * This is the basic method to write out an object graph rooted at |
| 265 * [object] and return the result. Right now this is hard-coded to return |
| 266 * a String from a custom [JSON] format, but that is likely to change to be |
| 267 * more pluggable in the near future. |
| 268 */ |
| 269 String write(Object object) { |
| 270 return newWriter().write(object); |
| 271 } |
| 272 |
| 273 /** |
| 274 * Return a new [Writer] object for this serialization. This is useful if you |
| 275 * want to do something more complex with the writer than just returning |
| 276 * the final result. |
| 277 */ |
| 278 Writer newWriter() => new Writer(this); |
| 279 |
| 280 /** |
| 281 * Write out the tree in a custom flat format, returning a list containing |
| 282 * only "simple" types: num, String, and bool. |
| 283 */ |
| 284 List writeFlat(Object object) { |
| 285 return newWriter().writeFlat(object); |
| 286 } |
| 287 |
| 288 /** |
| 289 * Read the serialized data from [input] and return a List of the root |
| 290 * objects from the result. If there are objects that need to be resolved |
| 291 * in the current context, they should be provided in [externals] as a |
| 292 * Map from names to values. In particular, in the current implementation |
| 293 * any class mirrors needed should be provided in [externals] using the |
| 294 * class name as a key. In addition to the [externals] map provided here, |
| 295 * values will be looked up in the [externalObjects] map. |
| 296 */ |
| 297 List read(String input, [Map externals = const {}]) { |
| 298 return newReader().read(input, externals); |
| 299 } |
| 300 |
| 301 /** |
| 302 * In the most common case there is only a single root object to be read, |
| 303 * and this method can be used to return just one object rather than |
| 304 * a List. The [input] and [externals] parameters are the same as for the |
| 305 * general [read] method. |
| 306 */ |
| 307 Object readOne(String input, [Map externals = const {}]) { |
| 308 return newReader().readOne(input, externals); |
| 309 } |
| 310 |
| 311 /** |
| 312 * Return a new [Reader] object for this serialization. This is useful if |
| 313 * you want to do something more complex with the reader than just returning |
| 314 * the final result. |
| 315 */ |
| 316 Reader newReader() => new Reader(this); |
| 317 |
| 318 /** |
| 319 * Return the list of SerializationRule that apply to [object]. For |
| 320 * internal use, but public because it's used in testing. |
| 321 */ |
| 322 List<SerializationRule> rulesFor(object) { |
| 323 // This has a couple of edge cases. |
| 324 // 1) The owning object may have indicated we should use a different |
| 325 // rule than the default. |
| 326 // 2) We may not have a rule, in which case we lazily create a BasicRule. |
| 327 // 3) Rules are allowed to say mustBePrimary, meaning that they can be used |
| 328 // iff no other rule was chosen first. |
| 329 // TODO(alanknight): Can the mustBePrimary mechanism be removed or changed. |
| 330 // It adds an order dependency to the rules, and is messy. Reconsider in the |
| 331 // light of a more general mechanism for multiple rules per object. |
| 332 // TODO(alanknight): Finding which rules apply seems likely to be a |
| 333 // bottleneck, particularly with the current reflective implementation. |
| 334 // Consider how to improve it. e.g. cache the list of rules by class. But |
| 335 // be careful of issues like rules which have arbitrary predicates. Or |
| 336 // consider having the arbitrary predicates be secondary to an initial |
| 337 // class-based lookup mechanism. |
| 338 var target, candidateRules; |
| 339 if (object is DesignatedRuleForObject) { |
| 340 target = object.target; |
| 341 candidateRules = object.possibleRules(rules); |
| 342 } else { |
| 343 target = object; |
| 344 candidateRules = rules; |
| 345 } |
| 346 List applicable = candidateRules.filter((each) => each.appliesTo(target)); |
| 347 |
| 348 if (applicable.isEmpty) { |
| 349 return [addRuleFor(target)]; |
| 350 } |
| 351 |
| 352 if (applicable.length == 1) return applicable; |
| 353 var first = applicable[0]; |
| 354 var finalRules = applicable.filter( |
| 355 (x) => !x.mustBePrimary || (x == first)); |
| 356 |
| 357 if (finalRules.isEmpty) throw new SerializationException( |
| 358 'No valid rule found for object $object'); |
| 359 return finalRules; |
| 360 } |
| 361 |
| 362 /** |
| 363 * Create a Serialization for serializing SerializationRules. This is used |
| 364 * to save the rules in a self-describing format along with the data. |
| 365 * If there are new rule classes created, they will need to be described |
| 366 * here. |
| 367 */ |
| 368 Serialization _ruleSerialization() { |
| 369 // TODO(alanknight): There's an extensibility issue here with new rules. |
| 370 // TODO(alanknight): How to handle rules with closures? They have to |
| 371 // exist on the other side, but we might be able to hook them up by name, |
| 372 // or we might just be able to validate that they're correctly set up |
| 373 // on the other side. |
| 374 |
| 375 // Make some bogus rule instances so we have something to feed rule creation |
| 376 // and get their types. If only we had class literals implemented... |
| 377 var closureRule = new ClosureToMapRule.stub([].runtimeType); |
| 378 var basicRule = new BasicRule(reflect(null).type, '', [], [], []); |
| 379 |
| 380 var meta = new Serialization() |
| 381 ..selfDescribing = false |
| 382 ..addRuleFor(new ListRule()) |
| 383 ..addRuleFor(new PrimitiveRule()) |
| 384 ..addRuleFor(new ListRuleEssential()) |
| 385 ..addRuleFor(basicRule, |
| 386 constructorFields: ['typeWrapped', |
| 387 'constructorName', |
| 388 'constructorFields', 'regularFields', []], |
| 389 fields: []) |
| 390 ..addRule(new ClassMirrorRule()); |
| 391 meta.externalObjects = externalObjects; |
| 392 return meta; |
| 393 } |
| 394 } |
| 395 |
| 396 /** |
| 397 * An exception class for errors during serialization. |
| 398 */ |
| 399 class SerializationException implements Exception { |
| 400 final String message; |
| 401 const SerializationException([this.message]); |
| 402 } |
| OLD | NEW |