1 /** 2 Generic serialization framework. 3 4 This module provides general means for implementing (de-)serialization with 5 a standardized behavior. 6 7 Supported_types: 8 The following rules are applied in order when serializing or 9 deserializing a certain type: 10 11 $(OL 12 $(LI An `enum` type is serialized as its raw value, except if 13 `@byName` is used, in which case the name of the enum value 14 is serialized.) 15 $(LI Any type that is specifically supported by the serializer 16 is directly serialized. For example, the BSON serializer 17 supports `BsonObjectID` directly.) 18 $(LI Arrays and tuples (`std.typecons.Tuple`) are serialized 19 using the array serialization functions where each element is 20 serialized again according to these rules.) 21 $(LI Associative arrays are serialized similar to arrays. The key 22 type of the AA must satisfy the `isStringSerializable` trait 23 and will always be serialized as a string.) 24 $(LI Any `Nullable!T` will be serialized as either `null`, or 25 as the contained value (subject to these rules again).) 26 $(LI Any `Typedef!T` will be serialized as if it were just `T`.) 27 $(LI Any `BitFlags!T` value will be serialized as `T[]`) 28 $(LI Types satisfying the `isPolicySerializable` trait for the 29 supplied `Policy` will be serialized as the value returned 30 by the policy `toRepresentation` function (again subject to 31 these rules).) 32 $(LI Types satisfying the `isCustomSerializable` trait will be 33 serialized as the value returned by their `toRepresentation` 34 method (again subject to these rules).) 35 $(LI Types satisfying the `isISOExtStringSerializable` trait will be 36 serialized as a string, as returned by their `toISOExtString` 37 method. This causes types such as `SysTime` to be serialized 38 as strings.) 39 $(LI Types satisfying the `isStringSerializable` trait will be 40 serialized as a string, as returned by their `toString` 41 method.) 42 $(LI Struct and class types by default will be serialized as 43 associative arrays, where the key is the name of the 44 corresponding field (can be overridden using the `@name` 45 attribute). If the struct/class is annotated with `@asArray`, 46 it will instead be serialized as a flat array of values in the 47 order of declaration. Null class references will be serialized 48 as `null`.) 49 $(LI Pointer types will be serialized as either `null`, or as 50 the value they point to.) 51 $(LI Built-in integers and floating point values, as well as 52 boolean values will be converted to strings, if the serializer 53 doesn't support them directly.) 54 ) 55 56 Note that no aliasing detection is performed, so that pointers, class 57 references and arrays referencing the same memory will be serialized 58 as multiple copies. When in turn deserializing the data, they will also 59 end up as separate copies in memory. 60 61 Field_names: 62 By default, the field name of the serialized D type (for `struct` and 63 `class` aggregates) is represented as-is in the serialized result. To 64 circumvent name clashes with D's keywords, a single trailing underscore of 65 any field name is stipped, so that a field name of `version_` results in 66 just `"version"` as the serialized value. Names can also be freely 67 customized using the `@name` annotation. 68 69 Associative array keys are always represented using their direct string 70 representation. 71 72 Serializer_implementation: 73 Serializers are implemented in terms of a struct with template methods that 74 get called by the serialization framework: 75 76 --- 77 struct ExampleSerializer { 78 enum isSupportedValueType(T) = is(T == string) || is(T == typeof(null)); 79 80 // serialization 81 auto getSerializedResult(); 82 void beginWriteDocument(TypeTraits)(); 83 void endWriteDocument(TypeTraits)(); 84 void beginWriteDictionary(TypeTraits)(size_t length); [OR] void beginWriteDictionary(TypeTraits)(); 85 void endWriteDictionary(TypeTraits)(); 86 void beginWriteDictionaryEntry(ElementTypeTraits)(string name); 87 void endWriteDictionaryEntry(ElementTypeTraits)(string name); 88 void beginWriteArray(TypeTraits)(size_t length); 89 void endWriteArray(TypeTraits)(); 90 void beginWriteArrayEntry(ElementTypeTraits)(size_t index); 91 void endWriteArrayEntry(ElementTypeTraits)(size_t index); 92 void writeValue(TypeTraits, T)(T value); 93 94 // deserialization 95 void readDictionary(TypeTraits)(scope void delegate(string) entry_callback); 96 void beginReadDictionaryEntry(ElementTypeTraits)(string); 97 void endReadDictionaryEntry(ElementTypeTraits)(string); 98 void readArray(TypeTraits)(scope void delegate(size_t) size_callback, scope void delegate() entry_callback); 99 void beginReadArrayEntry(ElementTypeTraits)(size_t index); 100 void endReadArrayEntry(ElementTypeTraits)(size_t index); 101 T readValue(TypeTraits, T)(); 102 bool tryReadNull(TypeTraits)(); 103 } 104 --- 105 106 The `TypeTraits` type passed to the individual methods has the following members: 107 $(UL 108 $(LI `Type`: The original type of the field to serialize) 109 $(LI `Attributes`: User defined attributes attached to the field) 110 $(LI `Policy`: An alias to the policy used for the serialization process) 111 ) 112 113 `ElementTypeTraits` have the following additional members: 114 $(UL 115 $(LI `ContainerType`: The original type of the enclosing container type) 116 $(LI `ContainerAttributes`: User defined attributes attached to the enclosing container) 117 ) 118 119 Copyright: © 2013-2016 rejectedsoftware e.K. 120 License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file. 121 Authors: Sönke Ludwig 122 */ 123 module dutils.data.utils.serialization; 124 125 import std.array : Appender, appender; 126 import std.conv : to; 127 import std.exception : enforce; 128 import std.traits; 129 import std.typetuple; 130 131 import dutils.data.utils.traits; 132 import dutils.data.utils.uda; 133 134 /** 135 Serializes a value with the given serializer. 136 137 The serializer must have a value result for the first form 138 to work. Otherwise, use the range based form. 139 140 See_Also: `dutils.data.json.JSONSerializer`, `dutils.data.json.JSONStringSerializer`, `dutils.data.bson.BsonSerializer` 141 */ 142 auto serialize(Serializer, T, ARGS...)(auto ref T value, ARGS args) { 143 auto serializer = Serializer(args); 144 serialize(serializer, value); 145 return serializer.getSerializedResult(); 146 } 147 /// ditto 148 void serialize(Serializer, T)(ref Serializer serializer, auto ref T value) { 149 serializeWithPolicy!(Serializer, DefaultPolicy)(serializer, value); 150 } 151 152 /** Note that there is a convenience function `dutils.data.json.serializeToJSON` 153 that can be used instead of manually invoking `serialize`. 154 */ 155 unittest { 156 import dutils.data.json; 157 158 struct Test { 159 int value; 160 string text; 161 } 162 163 Test test; 164 test.value = 12; 165 test.text = "Hello"; 166 167 JSON serialized = serialize!JSONSerializer(test); 168 assert(serialized["value"].get!int == 12); 169 assert(serialized["text"].get!string == "Hello"); 170 } 171 172 unittest { 173 import dutils.data.json; 174 175 // Make sure that immutable(char[]) works just like string 176 // (i.e., immutable(char)[]). 177 immutable key = "answer"; 178 auto ints = [key : 42]; 179 auto serialized = serialize!JSONSerializer(ints); 180 assert(serialized[key].get!int == 42); 181 } 182 183 /** 184 Serializes a value with the given serializer, representing values according to `Policy` when possible. 185 186 The serializer must have a value result for the first form 187 to work. Otherwise, use the range based form. 188 189 See_Also: `dutils.data.json.JSONSerializer`, `dutils.data.json.JSONStringSerializer`, `dutils.data.bson.BsonSerializer` 190 */ 191 private auto serializeWithPolicy(Serializer, alias Policy, T, ARGS...)(auto ref T value, ARGS args) { 192 auto serializer = Serializer(args); 193 serializeWithPolicy!(Serializer, Policy)(serializer, value); 194 return serializer.getSerializedResult(); 195 } 196 /// ditto 197 private void serializeWithPolicy(Serializer, alias Policy, T)( 198 ref Serializer serializer, auto ref T value) { 199 static if (is(typeof(serializer.beginWriteDocument!T()))) 200 serializer.beginWriteDocument!T(); 201 serializeValueImpl!(Serializer, Policy).serializeValue!T(serializer, value); 202 static if (is(typeof(serializer.endWriteDocument!T()))) 203 serializer.endWriteDocument!T(); 204 } 205 /// 206 version (unittest) { 207 } 208 209 /// 210 unittest { 211 import dutils.data.json; 212 213 template SizePol(T) if (__traits(allMembers, T) == TypeTuple!("x", "y")) { 214 import std.conv; 215 import std.array; 216 217 static string toRepresentation(T value) @safe { 218 return to!string(value.x) ~ "x" ~ to!string(value.y); 219 } 220 221 static T fromRepresentation(string value) { 222 string[] fields = value.split('x'); 223 alias fieldT = typeof(T.x); 224 auto x = to!fieldT(fields[0]); 225 auto y = to!fieldT(fields[1]); 226 return T(x, y); 227 } 228 } 229 230 static struct SizeI { 231 int x; 232 int y; 233 } 234 235 SizeI sizeI = SizeI(1, 2); 236 JSON serializedI = serializeWithPolicy!(JSONSerializer, SizePol)(sizeI); 237 assert(serializedI.get!string == "1x2"); 238 239 static struct SizeF { 240 float x; 241 float y; 242 } 243 244 SizeF sizeF = SizeF(0.1f, 0.2f); 245 JSON serializedF = serializeWithPolicy!(JSONSerializer, SizePol)(sizeF); 246 assert(serializedF.get!string == "0.1x0.2"); 247 } 248 249 /** 250 Deserializes and returns a serialized value. 251 252 serialized_data can be either an input range or a value containing 253 the serialized data, depending on the type of serializer used. 254 255 See_Also: `dutils.data.json.JSONSerializer`, `dutils.data.json.JSONStringSerializer`, `dutils.data.bson.BsonSerializer` 256 */ 257 T deserialize(Serializer, T, ARGS...)(ARGS args) { 258 return deserializeWithPolicy!(Serializer, DefaultPolicy, T)(args); 259 } 260 261 /** Note that there is a convenience function `dutils.data.json.deserializeJSON` 262 that can be used instead of manually invoking `deserialize`. 263 */ 264 unittest { 265 import dutils.data.json; 266 267 struct Test { 268 int value; 269 string text; 270 } 271 272 JSON serialized = JSON.emptyObject; 273 serialized["value"] = 12; 274 serialized["text"] = "Hello"; 275 276 Test test = deserialize!(JSONSerializer, Test)(serialized); 277 assert(test.value == 12); 278 assert(test.text == "Hello"); 279 } 280 281 /** 282 Deserializes and returns a serialized value, interpreting values according to `Policy` when possible. 283 284 serialized_data can be either an input range or a value containing 285 the serialized data, depending on the type of serializer used. 286 287 See_Also: `dutils.data.json.JSONSerializer`, `dutils.data.json.JSONStringSerializer`, `dutils.data.bson.BsonSerializer` 288 */ 289 private T deserializeWithPolicy(Serializer, alias Policy, T, ARGS...)(ARGS args) { 290 auto deserializer = Serializer(args); 291 return deserializeValueImpl!(Serializer, Policy).deserializeValue!T(deserializer); 292 } 293 294 /// 295 unittest { 296 import dutils.data.json; 297 298 template SizePol(T) if (__traits(allMembers, T) == TypeTuple!("x", "y")) { 299 import std.conv; 300 import std.array; 301 302 static string toRepresentation(T value) @safe { 303 return to!string(value.x) ~ "x" ~ to!string(value.y); 304 } 305 306 static T fromRepresentation(string value) @safe { 307 string[] fields = value.split('x'); 308 alias fieldT = typeof(T.x); 309 auto x = to!fieldT(fields[0]); 310 auto y = to!fieldT(fields[1]); 311 return T(x, y); 312 } 313 } 314 315 static struct SizeI { 316 int x; 317 int y; 318 } 319 320 JSON serializedI = "1x2"; 321 SizeI sizeI = deserializeWithPolicy!(JSONSerializer, SizePol, SizeI)(serializedI); 322 assert(sizeI.x == 1); 323 assert(sizeI.y == 2); 324 325 static struct SizeF { 326 float x; 327 float y; 328 } 329 330 JSON serializedF = "0.1x0.2"; 331 SizeF sizeF = deserializeWithPolicy!(JSONSerializer, SizePol, SizeF)(serializedF); 332 assert(sizeF.x == 0.1f); 333 assert(sizeF.y == 0.2f); 334 } 335 336 private template serializeValueImpl(Serializer, alias Policy) { 337 alias _Policy = Policy; 338 static assert(Serializer.isSupportedValueType!string, 339 "All serializers must support string values."); 340 static assert(Serializer.isSupportedValueType!(typeof(null)), 341 "All serializers must support null values."); 342 343 // work around https://issues.dlang.org/show_bug.cgi?id=16528 344 static if (isSafeSerializer!Serializer) { 345 void serializeValue(T, ATTRIBUTES...)(ref Serializer ser, auto ref T value) @safe { 346 serializeValueDeduced!(T, ATTRIBUTES)(ser, value); 347 } 348 } else { 349 void serializeValue(T, ATTRIBUTES...)(ref Serializer ser, auto ref T value) { 350 serializeValueDeduced!(T, ATTRIBUTES)(ser, value); 351 } 352 } 353 354 private void serializeValueDeduced(T, ATTRIBUTES...)(ref Serializer ser, auto ref T value) { 355 import std.typecons : BitFlags, Nullable, Tuple, Typedef, TypedefType, tuple; 356 357 alias TU = Unqual!T; 358 359 alias Traits = .Traits!(TU, _Policy, ATTRIBUTES); 360 361 static if (isPolicySerializable!(Policy, TU)) { 362 alias CustomType = typeof(Policy!TU.toRepresentation(TU.init)); 363 ser.serializeValue!(CustomType, ATTRIBUTES)(Policy!TU.toRepresentation(value)); 364 } else static if (is(TU == enum)) { 365 static if (hasPolicyAttributeL!(ByNameAttribute, Policy, ATTRIBUTES)) { 366 ser.serializeValue!(string)(value.to!string()); 367 } else { 368 ser.serializeValue!(OriginalType!TU)(cast(OriginalType!TU) value); 369 } 370 } else static if (Serializer.isSupportedValueType!TU) { 371 static if (is(TU == typeof(null))) 372 ser.writeValue!Traits(null); 373 else 374 ser.writeValue!(Traits)(value); 375 } else static if ( /*isInstanceOf!(Tuple, TU)*/ is(T == Tuple!TPS, TPS...)) { 376 import std.algorithm.searching : all; 377 378 static if (all!"!a.empty"([TU.fieldNames]) 379 && !hasPolicyAttributeL!(AsArrayAttribute, Policy, ATTRIBUTES)) { 380 static if (__traits(compiles, ser.beginWriteDictionary!TU(0))) { 381 auto nfields = value.length; 382 ser.beginWriteDictionary!Traits(nfields); 383 } else { 384 ser.beginWriteDictionary!Traits(); 385 } 386 foreach (i, _; T.Types) { 387 alias TV = typeof(value[i]); 388 alias STraits = SubTraits!(Traits, TV); 389 ser.beginWriteDictionaryEntry!STraits(underscoreStrip(TU.fieldNames[i])); 390 ser.serializeValue!(TV, ATTRIBUTES)(value[i]); 391 ser.endWriteDictionaryEntry!STraits(underscoreStrip(TU.fieldNames[i])); 392 } 393 static if (__traits(compiles, ser.endWriteDictionary!TU(0))) { 394 ser.endWriteDictionary!Traits(nfields); 395 } else { 396 ser.endWriteDictionary!Traits(); 397 } 398 } else static if (TU.Types.length == 1) { 399 ser.serializeValue!(typeof(value[0]), ATTRIBUTES)(value[0]); 400 } else { 401 ser.beginWriteArray!Traits(value.length); 402 foreach (i, _; T.Types) { 403 alias TV = typeof(value[i]); 404 alias STraits = SubTraits!(Traits, TV); 405 ser.beginWriteArrayEntry!STraits(i); 406 ser.serializeValue!(TV, ATTRIBUTES)(value[i]); 407 ser.endWriteArrayEntry!STraits(i); 408 } 409 ser.endWriteArray!Traits(); 410 } 411 } else static if (isArray!TU) { 412 alias TV = typeof(value[0]); 413 alias STraits = SubTraits!(Traits, TV); 414 ser.beginWriteArray!Traits(value.length); 415 foreach (i, ref el; value) { 416 ser.beginWriteArrayEntry!STraits(i); 417 ser.serializeValue!(TV, ATTRIBUTES)(el); 418 ser.endWriteArrayEntry!STraits(i); 419 } 420 ser.endWriteArray!Traits(); 421 } else static if (isAssociativeArray!TU) { 422 alias TK = KeyType!TU; 423 alias TV = ValueType!TU; 424 alias STraits = SubTraits!(Traits, TV); 425 426 static if (__traits(compiles, ser.beginWriteDictionary!TU(0))) { 427 auto nfields = value.length; 428 ser.beginWriteDictionary!Traits(nfields); 429 } else { 430 ser.beginWriteDictionary!Traits(); 431 } 432 foreach (key, ref el; value) { 433 string keyname; 434 static if (is(TK : string)) 435 keyname = key; 436 else static if (is(TK : real) || is(TK : long) || is(TK == enum)) 437 keyname = key.to!string; 438 else static if (isStringSerializable!TK) 439 keyname = key.toString(); 440 else 441 static assert(false, 442 "Associative array keys must be strings, numbers, enums, or have toString/fromString methods."); 443 ser.beginWriteDictionaryEntry!STraits(keyname); 444 ser.serializeValue!(TV, ATTRIBUTES)(el); 445 ser.endWriteDictionaryEntry!STraits(keyname); 446 } 447 static if (__traits(compiles, ser.endWriteDictionary!TU(0))) { 448 ser.endWriteDictionary!Traits(nfields); 449 } else { 450 ser.endWriteDictionary!Traits(); 451 } 452 } else static if ( /*isInstanceOf!(Nullable, TU)*/ is(T == Nullable!TPS, TPS...)) { 453 if (value.isNull()) 454 ser.serializeValue!(typeof(null))(null); 455 else 456 ser.serializeValue!(typeof(value.get()), ATTRIBUTES)(value.get()); 457 } else static if (isInstanceOf!(Typedef, TU)) { 458 ser.serializeValue!(TypedefType!TU, ATTRIBUTES)(cast(TypedefType!TU) value); 459 } else static if (is(TU == BitFlags!E, E)) { 460 alias STraits = SubTraits!(Traits, E); 461 462 size_t cnt = 0; 463 foreach (v; EnumMembers!E) 464 if (value & v) 465 cnt++; 466 467 ser.beginWriteArray!Traits(cnt); 468 cnt = 0; 469 foreach (v; EnumMembers!E) 470 if (value & v) { 471 ser.beginWriteArrayEntry!STraits(cnt); 472 ser.serializeValue!(E, ATTRIBUTES)(v); 473 ser.endWriteArrayEntry!STraits(cnt); 474 cnt++; 475 } 476 ser.endWriteArray!Traits(); 477 } else static if (isCustomSerializable!TU) { 478 alias CustomType = typeof(T.init.toRepresentation()); 479 ser.serializeValue!(CustomType, ATTRIBUTES)(value.toRepresentation()); 480 } else static if (isISOExtStringSerializable!TU) { 481 ser.serializeValue!(string, ATTRIBUTES)(value.toISOExtString()); 482 } else static if (isStringSerializable!TU) { 483 ser.serializeValue!(string, ATTRIBUTES)(value.toString()); 484 } else static if (is(TU == struct) || is(TU == class)) { 485 static if (!hasSerializableFields!(TU, Policy)) 486 pragma(msg, "Serializing composite type " ~ T.stringof ~ " which has no serializable fields"); 487 static if (is(TU == class)) { 488 if (value is null) { 489 ser.serializeValue!(typeof(null))(null); 490 return; 491 } 492 } 493 static auto safeGetMember(string mname)(ref T val) @safe { 494 static if (__traits(compiles, __traits(getMember, val, mname))) { 495 return __traits(getMember, val, mname); 496 } else { 497 pragma(msg, "Warning: Getter for " ~ fullyQualifiedName!T ~ "." ~ mname ~ " is not @safe"); 498 return () @trusted { return __traits(getMember, val, mname); }(); 499 } 500 } 501 502 static if (hasPolicyAttributeL!(AsArrayAttribute, Policy, ATTRIBUTES)) { 503 enum nfields = getExpandedFieldCount!(TU, SerializableFields!(TU, Policy)); 504 ser.beginWriteArray!Traits(nfields); 505 size_t fcount = 0; 506 foreach (mname; SerializableFields!(TU, Policy)) { 507 alias TMS = TypeTuple!(typeof(__traits(getMember, value, mname))); 508 foreach (j, TM; TMS) { 509 alias TA = TypeTuple!(__traits(getAttributes, 510 TypeTuple!(__traits(getMember, T, mname))[j])); 511 alias STraits = SubTraits!(Traits, TM, TA); 512 ser.beginWriteArrayEntry!STraits(fcount); 513 static if (!isBuiltinTuple!(T, mname)) 514 ser.serializeValue!(TM, TA)(safeGetMember!mname(value)); 515 else 516 ser.serializeValue!(TM, TA)(tuple(__traits(getMember, value, mname))[j]); 517 ser.endWriteArrayEntry!STraits(fcount); 518 fcount++; 519 } 520 } 521 ser.endWriteArray!Traits(); 522 } else { 523 static if (__traits(compiles, ser.beginWriteDictionary!Traits(0))) { 524 auto nfields = getExpandedFieldCount!(TU, SerializableFields!(TU, Policy)); 525 526 foreach (mname; SerializableFields!(TU, Policy)) { 527 static if (!isBuiltinTuple!(T, mname)) { 528 auto vt = safeGetMember!mname(value); 529 static if (is(typeof(vt) : Nullable!NVT, NVT) && hasPolicyAttribute!(EmbedNullableIgnoreNullAttribute, 530 Policy, TypeTuple!(__traits(getMember, T, mname))[0])) { 531 if (vt.isNull) 532 nfields--; 533 } 534 } 535 } 536 537 ser.beginWriteDictionary!Traits(nfields); 538 } else { 539 ser.beginWriteDictionary!Traits(); 540 } 541 foreach (mname; SerializableFields!(TU, Policy)) { 542 alias TM = TypeTuple!(typeof(__traits(getMember, TU, mname))); 543 alias TA = TypeTuple!(__traits(getAttributes, 544 TypeTuple!(__traits(getMember, T, mname))[0])); 545 enum name = getPolicyAttribute!(TU, mname, NameAttribute, Policy)( 546 NameAttribute!DefaultPolicy(underscoreStrip(mname))).name; 547 static if (!isBuiltinTuple!(T, mname)) { 548 auto vtn = safeGetMember!mname(value); 549 static if (is(typeof(vtn) : Nullable!NVT, NVT) && hasPolicyAttribute!(EmbedNullableIgnoreNullAttribute, 550 Policy, TypeTuple!(__traits(getMember, T, mname))[0])) { 551 if (vtn.isNull) 552 continue; 553 auto vt = vtn.get; 554 } else { 555 auto vt = vtn; 556 } 557 } else { 558 alias TTM = TypeTuple!(typeof(__traits(getMember, value, mname))); 559 auto vt = tuple!TTM(__traits(getMember, value, mname)); 560 } 561 alias STraits = SubTraits!(Traits, typeof(vt), TA); 562 ser.beginWriteDictionaryEntry!STraits(name); 563 ser.serializeValue!(typeof(vt), TA)(vt); 564 ser.endWriteDictionaryEntry!STraits(name); 565 } 566 static if (__traits(compiles, ser.endWriteDictionary!Traits(0))) { 567 ser.endWriteDictionary!Traits(nfields); 568 } else { 569 ser.endWriteDictionary!Traits(); 570 } 571 } 572 } else static if (isPointer!TU) { 573 if (value is null) { 574 ser.writeValue!Traits(null); 575 return; 576 } 577 ser.serializeValue!(PointerTarget!TU)(*value); 578 } else static if (is(TU == bool) || is(TU : real) || is(TU : long)) { 579 ser.serializeValue!(string, ATTRIBUTES)(to!string(value)); 580 } else 581 static assert(false, "Unsupported serialization type: " ~ T.stringof); 582 } 583 } 584 585 private struct Traits(T, alias POL, ATTRIBUTES...) { 586 alias Type = T; 587 alias Policy = POL; 588 alias Attributes = TypeTuple!ATTRIBUTES; 589 } 590 591 private struct SubTraits(Traits, T, A...) { 592 alias Type = Unqual!T; 593 alias Attributes = TypeTuple!A; 594 alias Policy = Traits.Policy; 595 alias ContainerType = Traits.Type; 596 alias ContainerAttributes = Traits.Attributes; 597 } 598 599 private template deserializeValueImpl(Serializer, alias Policy) { 600 alias _Policy = Policy; 601 static assert(Serializer.isSupportedValueType!string, 602 "All serializers must support string values."); 603 static assert(Serializer.isSupportedValueType!(typeof(null)), 604 "All serializers must support null values."); 605 606 // work around https://issues.dlang.org/show_bug.cgi?id=16528 607 static if (isSafeDeserializer!Serializer) { 608 T deserializeValue(T, ATTRIBUTES...)(ref Serializer ser) @safe { 609 return deserializeValueDeduced!(T, ATTRIBUTES)(ser); 610 } 611 } else { 612 T deserializeValue(T, ATTRIBUTES...)(ref Serializer ser) { 613 return deserializeValueDeduced!(T, ATTRIBUTES)(ser); 614 } 615 } 616 617 T deserializeValueDeduced(T, ATTRIBUTES...)(ref Serializer ser) if (!isMutable!T) { 618 import std.algorithm.mutation : move; 619 620 auto ret = deserializeValue!(Unqual!T, ATTRIBUTES)(ser); 621 return () @trusted { return cast(T) ret.move; }(); 622 } 623 624 T deserializeValueDeduced(T, ATTRIBUTES...)(ref Serializer ser) if (isMutable!T) { 625 import std.typecons : BitFlags, Nullable, Typedef, TypedefType, Tuple; 626 627 alias Traits = .Traits!(T, _Policy, ATTRIBUTES); 628 629 static if (isPolicySerializable!(Policy, T)) { 630 alias CustomType = typeof(Policy!T.toRepresentation(T.init)); 631 return Policy!T.fromRepresentation(ser.deserializeValue!(CustomType, ATTRIBUTES)); 632 } else static if (is(T == enum)) { 633 static if (hasPolicyAttributeL!(ByNameAttribute, Policy, ATTRIBUTES)) { 634 return ser.deserializeValue!(string, ATTRIBUTES) 635 .to!T(); 636 } else { 637 return cast(T) ser.deserializeValue!(OriginalType!T); 638 } 639 } else static if (Serializer.isSupportedValueType!T) { 640 return ser.readValue!(Traits, T)(); 641 } else static if ( /*isInstanceOf!(Tuple, TU)*/ is(T == Tuple!TPS, TPS...)) { 642 enum fieldsCount = T.Types.length; 643 import std.algorithm.searching : all; 644 645 static if (all!"!a.empty"([T.fieldNames]) 646 && !hasPolicyAttributeL!(AsArrayAttribute, Policy, ATTRIBUTES)) { 647 T ret; 648 bool[fieldsCount] set; 649 ser.readDictionary!Traits((name) { 650 switch (name) { 651 default: 652 break; 653 foreach (i, TV; T.Types) { 654 enum fieldName = underscoreStrip(T.fieldNames[i]); 655 alias STraits = SubTraits!(Traits, TV); 656 case fieldName: { 657 ser.beginReadDictionaryEntry!STraits(fieldName); 658 ret[i] = ser.deserializeValue!(TV, ATTRIBUTES); 659 ser.endReadDictionaryEntry!STraits(fieldName); 660 set[i] = true; 661 } 662 break; 663 } 664 } 665 }); 666 foreach (i, fieldName; T.fieldNames) 667 enforce(set[i], 668 "Missing tuple field '" ~ fieldName ~ "' of type '" 669 ~ T.Types[i].stringof ~ "' (" ~ Policy.stringof ~ ")."); 670 return ret; 671 } else static if (fieldsCount == 1) { 672 return T(ser.deserializeValue!(T.Types[0], ATTRIBUTES)()); 673 } else { 674 T ret; 675 size_t currentField = 0; 676 ser.readArray!Traits((sz) { assert(sz == 0 || sz == fieldsCount); }, { 677 switch (currentField++) { 678 default: 679 break; 680 foreach (i, TV; T.Types) { 681 alias STraits = SubTraits!(Traits, TV); 682 case i: { 683 ser.beginReadArrayEntry!STraits(i); 684 ret[i] = ser.deserializeValue!(TV, ATTRIBUTES); 685 ser.endReadArrayEntry!STraits(i); 686 } 687 break; 688 } 689 } 690 }); 691 enforce(currentField == fieldsCount, 692 "Missing tuple field(s) - expected '" ~ fieldsCount.stringof 693 ~ "', received '" ~ currentField.stringof ~ "' (" ~ Policy.stringof ~ ")."); 694 return ret; 695 } 696 } else static if (isStaticArray!T) { 697 alias TV = typeof(T.init[0]); 698 alias STraits = SubTraits!(Traits, TV); 699 T ret; 700 size_t i = 0; 701 ser.readArray!Traits((sz) { assert(sz == 0 || sz == T.length); }, { 702 assert(i < T.length); 703 ser.beginReadArrayEntry!STraits(i); 704 ret[i] = ser.deserializeValue!(TV, ATTRIBUTES); 705 ser.endReadArrayEntry!STraits(i); 706 i++; 707 }); 708 return ret; 709 } else static if (isDynamicArray!T) { 710 alias TV = typeof(T.init[0]); 711 alias STraits = SubTraits!(Traits, TV); 712 //auto ret = appender!T(); 713 T ret; // Cannot use appender because of DMD BUG 10690/10859/11357 714 ser.readArray!Traits((sz) @safe { ret.reserve(sz); }, () @safe { 715 size_t i = ret.length; 716 ser.beginReadArrayEntry!STraits(i); 717 static if (__traits(compiles, ()@safe { 718 ser.deserializeValue!(TV, ATTRIBUTES); 719 })) 720 ret ~= ser.deserializeValue!(TV, ATTRIBUTES); 721 else // recursive array https://issues.dlang.org/show_bug.cgi?id=16528 722 ret ~= (() @trusted => ser.deserializeValue!(TV, ATTRIBUTES))(); 723 ser.endReadArrayEntry!STraits(i); 724 }); 725 return ret; //cast(T)ret.data; 726 } else static if (isAssociativeArray!T) { 727 alias TK = KeyType!T; 728 alias TV = ValueType!T; 729 alias STraits = SubTraits!(Traits, TV); 730 731 T ret; 732 ser.readDictionary!Traits((name) @safe { 733 TK key; 734 static if (is(TK == string) || (is(TK == enum) && is(OriginalType!TK == string))) 735 key = cast(TK) name; 736 else static if (is(TK : real) || is(TK : long) || is(TK == enum)) 737 key = name.to!TK; 738 else static if (isStringSerializable!TK) 739 key = TK.fromString(name); 740 else 741 static assert(false, 742 "Associative array keys must be strings, numbers, enums, or have toString/fromString methods."); 743 ser.beginReadDictionaryEntry!STraits(name); 744 ret[key] = ser.deserializeValue!(TV, ATTRIBUTES); 745 ser.endReadDictionaryEntry!STraits(name); 746 }); 747 return ret; 748 } else static if (isInstanceOf!(Nullable, T)) { 749 if (ser.tryReadNull!Traits()) 750 return T.init; 751 return T(ser.deserializeValue!(typeof(T.init.get()), ATTRIBUTES)); 752 } else static if (isInstanceOf!(Typedef, T)) { 753 return T(ser.deserializeValue!(TypedefType!T, ATTRIBUTES)); 754 } else static if (is(T == BitFlags!E, E)) { 755 alias STraits = SubTraits!(Traits, E); 756 T ret; 757 size_t i = 0; 758 ser.readArray!Traits((sz) {}, { 759 ser.beginReadArrayEntry!STraits(i); 760 ret |= ser.deserializeValue!(E, ATTRIBUTES); 761 ser.endReadArrayEntry!STraits(i); 762 i++; 763 }); 764 return ret; 765 } else static if (isCustomSerializable!T) { 766 alias CustomType = typeof(T.init.toRepresentation()); 767 return T.fromRepresentation(ser.deserializeValue!(CustomType, ATTRIBUTES)); 768 } else static if (isISOExtStringSerializable!T) { 769 return T.fromISOExtString(ser.readValue!(Traits, string)()); 770 } else static if (isStringSerializable!T) { 771 return T.fromString(ser.readValue!(Traits, string)()); 772 } else static if (is(T == struct) || is(T == class)) { 773 static if (is(T == class)) { 774 if (ser.tryReadNull!Traits()) 775 return null; 776 } 777 778 T ret; 779 string name; 780 bool[getExpandedFieldsData!(T, SerializableFields!(T, Policy)).length] set; 781 static if (is(T == class)) 782 ret = new T; 783 784 void safeSetMember(string mname, U)(ref T value, U fval) @safe { 785 static if (__traits(compiles, ()@safe { 786 __traits(getMember, value, mname) = fval; 787 })) 788 __traits(getMember, value, mname) = fval; 789 else { 790 pragma(msg, "Warning: Setter for " ~ fullyQualifiedName!T ~ "." ~ mname ~ " is not @safe"); 791 () @trusted { __traits(getMember, value, mname) = fval; }(); 792 } 793 } 794 795 static if (hasPolicyAttributeL!(AsArrayAttribute, Policy, ATTRIBUTES)) { 796 size_t idx = 0; 797 ser.readArray!Traits((sz) {}, { 798 static if (hasSerializableFields!(T, Policy)) { 799 switch (idx++) { 800 default: 801 break; 802 foreach (i, FD; getExpandedFieldsData!(T, SerializableFields!(T, Policy))) { 803 enum mname = FD[0]; 804 enum msindex = FD[1]; 805 alias MT = TypeTuple!(__traits(getMember, T, mname)); 806 alias MTI = MT[msindex]; 807 alias TMTI = typeof(MTI); 808 alias TMTIA = TypeTuple!(__traits(getAttributes, MTI)); 809 alias STraits = SubTraits!(Traits, TMTI, TMTIA); 810 811 case i: 812 static if (hasPolicyAttribute!(OptionalAttribute, Policy, MTI)) 813 if (ser.tryReadNull!STraits()) 814 return; 815 set[i] = true; 816 ser.beginReadArrayEntry!STraits(i); 817 static if (!isBuiltinTuple!(T, mname)) { 818 safeSetMember!mname(ret, ser.deserializeValue!(TMTI, TMTIA)); 819 } else { 820 __traits(getMember, ret, mname)[msindex] = ser.deserializeValue!(TMTI, TMTIA); 821 } 822 ser.endReadArrayEntry!STraits(i); 823 break; 824 } 825 } 826 } else { 827 pragma(msg, 828 "Deserializing composite type " ~ T.stringof ~ " which has no serializable fields."); 829 } 830 }); 831 } else { 832 ser.readDictionary!Traits((name) { 833 static if (hasSerializableFields!(T, Policy)) { 834 switch (name) { 835 default: 836 break; 837 foreach (i, mname; SerializableFields!(T, Policy)) { 838 alias TM = TypeTuple!(typeof(__traits(getMember, T, mname))); 839 alias TA = TypeTuple!(__traits(getAttributes, 840 TypeTuple!(__traits(getMember, T, mname))[0])); 841 alias STraits = SubTraits!(Traits, TM, TA); 842 enum fname = getPolicyAttribute!(T, mname, NameAttribute, Policy)( 843 NameAttribute!DefaultPolicy(underscoreStrip(mname))).name; 844 case fname: 845 static if (hasPolicyAttribute!(OptionalAttribute, Policy, 846 TypeTuple!(__traits(getMember, T, mname))[0])) 847 if (ser.tryReadNull!STraits()) 848 return; 849 set[i] = true; 850 ser.beginReadDictionaryEntry!STraits(fname); 851 static if (!isBuiltinTuple!(T, mname)) { 852 safeSetMember!mname(ret, ser.deserializeValue!(TM, TA)); 853 } else { 854 __traits(getMember, ret, mname) = ser.deserializeValue!(Tuple!TM, TA); 855 } 856 ser.endReadDictionaryEntry!STraits(fname); 857 break; 858 } 859 } 860 } else { 861 pragma(msg, 862 "Deserializing composite type " ~ T.stringof ~ " which has no serializable fields."); 863 } 864 }); 865 } 866 foreach (i, mname; SerializableFields!(T, Policy)) 867 static if (!hasPolicyAttribute!(OptionalAttribute, Policy, 868 TypeTuple!(__traits(getMember, T, mname))[0])) 869 enforce(set[i], 870 "Missing non-optional field '" ~ mname ~ "' of type '" ~ T.stringof 871 ~ "' (" ~ Policy.stringof ~ ")."); 872 return ret; 873 } else static if (isPointer!T) { 874 if (ser.tryReadNull!Traits()) 875 return null; 876 alias PT = PointerTarget!T; 877 auto ret = new PT; 878 *ret = ser.deserializeValue!(PT, ATTRIBUTES); 879 return ret; 880 } else static if (is(T == bool) || is(T : real) || is(T : long)) { 881 return to!T(ser.deserializeValue!string()); 882 } else 883 static assert(false, "Unsupported serialization type: " ~ T.stringof); 884 } 885 } 886 887 /** 888 Attribute for overriding the field name during (de-)serialization. 889 890 Note that without the `@name` attribute there is a shorter alternative 891 for using names that collide with a D keyword. A single trailing 892 underscore will automatically be stripped when determining a field 893 name. 894 */ 895 private NameAttribute!Policy name(alias Policy = DefaultPolicy)(string name) { 896 return NameAttribute!Policy(name); 897 } 898 /// 899 unittest { 900 struct CustomPolicy { 901 } 902 903 struct Test { 904 // serialized as "screen-size": 905 @name("screen-size") int screenSize; 906 907 // serialized as "print-size" by default, 908 // but as "PRINTSIZE" if CustomPolicy is used for serialization. 909 @name("print-size") 910 @name!CustomPolicy("PRINTSIZE") int printSize; 911 912 // serialized as "version" 913 int version_; 914 } 915 } 916 917 /** 918 Attribute marking a field as optional during deserialization. 919 */ 920 @property OptionalAttribute!Policy optional(alias Policy = DefaultPolicy)() { 921 return OptionalAttribute!Policy(); 922 } 923 /// 924 unittest { 925 struct Test { 926 // does not need to be present during deserialization 927 @optional int screenSize = 100; 928 } 929 } 930 931 /** 932 Attribute for marking non-serialized fields. 933 */ 934 private @property IgnoreAttribute!Policy ignore(alias Policy = DefaultPolicy)() { 935 return IgnoreAttribute!Policy(); 936 } 937 /// 938 unittest { 939 struct Test { 940 // is neither serialized not deserialized 941 @ignore int screenSize; 942 } 943 } 944 /// 945 unittest { 946 template CustomPolicy(T) { 947 // ... 948 } 949 950 struct Test { 951 // not (de)serialized for serializeWithPolicy!(Test, CustomPolicy) 952 // but for other policies or when serialized without a policy 953 @ignore!CustomPolicy int screenSize; 954 } 955 } 956 957 /** 958 Attribute for forcing serialization of enum fields by name instead of by value. 959 */ 960 private @property ByNameAttribute!Policy byName(alias Policy = DefaultPolicy)() { 961 return ByNameAttribute!Policy(); 962 } 963 /// 964 unittest { 965 enum Color { 966 red, 967 green, 968 blue 969 } 970 971 struct Test { 972 // serialized as an int (e.g. 1 for Color.green) 973 Color color; 974 // serialized as a string (e.g. "green" for Color.green) 975 @byName Color namedColor; 976 // serialized as array of ints 977 Color[] colorArray; 978 // serialized as array of strings 979 @byName Color[] namedColorArray; 980 } 981 } 982 983 /** 984 Attribute for representing a struct/class as an array instead of an object. 985 986 Usually structs and class objects are serialized as dictionaries mapping 987 from field name to value. Using this attribute, they will be serialized 988 as a flat array instead. Note that changing the layout will make any 989 already serialized data mismatch when this attribute is used. 990 */ 991 @property AsArrayAttribute!Policy asArray(alias Policy = DefaultPolicy)() { 992 return AsArrayAttribute!Policy(); 993 } 994 /// 995 unittest { 996 struct Fields { 997 int f1; 998 string f2; 999 double f3; 1000 } 1001 1002 struct Test { 1003 // serialized as name:value pairs ["f1": int, "f2": string, "f3": double] 1004 Fields object; 1005 // serialized as a sequential list of values [int, string, double] 1006 @asArray Fields array; 1007 } 1008 1009 import dutils.data.json; 1010 1011 static assert(is(typeof(serializeToJSON(Test())))); 1012 } 1013 1014 /** 1015 Makes this nullable as if it is not a nullable to the serializer. Ignores the field completely when it is null. 1016 1017 Works with Nullable!classes and Nullable!structs. Behavior is undefined if this is applied to other types. 1018 1019 Implicitly marks this as optional for deserialization. (Keeps the struct default value when not present in serialized value) 1020 */ 1021 private @property EmbedNullableIgnoreNullAttribute!Policy embedNullable(alias Policy = DefaultPolicy)() { 1022 return EmbedNullableIgnoreNullAttribute!Policy(); 1023 } 1024 /// 1025 unittest { 1026 import std.typecons : Nullable; 1027 1028 struct Test { 1029 // Not serialized at all if null, ignored on deserialization if not present. 1030 @embedNullable Nullable!int field; 1031 } 1032 } 1033 1034 /// 1035 private enum FieldExistence { 1036 missing, 1037 exists, 1038 defer 1039 } 1040 1041 /// User defined attribute (not intended for direct use) 1042 private struct NameAttribute(alias POLICY) { 1043 alias Policy = POLICY; 1044 string name; 1045 } 1046 /// ditto 1047 private struct OptionalAttribute(alias POLICY) { 1048 alias Policy = POLICY; 1049 } 1050 /// ditto 1051 private struct IgnoreAttribute(alias POLICY) { 1052 alias Policy = POLICY; 1053 } 1054 /// ditto 1055 private struct ByNameAttribute(alias POLICY) { 1056 alias Policy = POLICY; 1057 } 1058 /// ditto 1059 private struct AsArrayAttribute(alias POLICY) { 1060 alias Policy = POLICY; 1061 } 1062 /// ditto 1063 private struct EmbedNullableIgnoreNullAttribute(alias POLICY) { 1064 alias Policy = POLICY; 1065 } 1066 1067 /** 1068 Checks if a given type has a custom serialization representation. 1069 1070 A class or struct type is custom serializable if it defines a pair of 1071 `toRepresentation`/`fromRepresentation` methods. Any class or 1072 struct type that has this trait will be serialized by using the return 1073 value of it's `toRepresentation` method instead of the original value. 1074 1075 This trait has precedence over `isISOExtStringSerializable` and 1076 `isStringSerializable`. 1077 */ 1078 private template isCustomSerializable(T) { 1079 enum bool isCustomSerializable = is(typeof(T.init.toRepresentation())) 1080 && is(typeof(T.fromRepresentation(T.init.toRepresentation())) == T); 1081 } 1082 /// 1083 unittest { 1084 // represented as a single uint when serialized 1085 static struct S { 1086 ushort x, y; 1087 1088 uint toRepresentation() const { 1089 return x + (y << 16); 1090 } 1091 1092 static S fromRepresentation(uint i) { 1093 return S(i & 0xFFFF, i >> 16); 1094 } 1095 } 1096 1097 static assert(isCustomSerializable!S); 1098 } 1099 1100 /** 1101 Checks if a given type has an ISO extended string serialization representation. 1102 1103 A class or struct type is ISO extended string serializable if it defines a 1104 pair of `toISOExtString`/`fromISOExtString` methods. Any class or 1105 struct type that has this trait will be serialized by using the return 1106 value of it's `toISOExtString` method instead of the original value. 1107 1108 This is mainly useful for supporting serialization of the the date/time 1109 types in `std.datetime`. 1110 1111 This trait has precedence over `isStringSerializable`. 1112 */ 1113 private template isISOExtStringSerializable(T) { 1114 enum bool isISOExtStringSerializable = is(typeof(T.init.toISOExtString()) : string) 1115 && is(typeof(T.fromISOExtString("")) : T); 1116 } 1117 /// 1118 unittest { 1119 import std.datetime; 1120 1121 static assert(isISOExtStringSerializable!DateTime); 1122 static assert(isISOExtStringSerializable!SysTime); 1123 1124 // represented as an ISO extended string when serialized 1125 static struct S { 1126 // dummy example implementations 1127 string toISOExtString() const { 1128 return ""; 1129 } 1130 1131 static S fromISOExtString(string s) { 1132 return S.init; 1133 } 1134 } 1135 1136 static assert(isISOExtStringSerializable!S); 1137 } 1138 1139 /** 1140 Checks if a given type has a string serialization representation. 1141 1142 A class or struct type is string serializable if it defines a pair of 1143 `toString`/`fromString` methods. Any class or struct type that 1144 has this trait will be serialized by using the return value of it's 1145 `toString` method instead of the original value. 1146 */ 1147 template isStringSerializable(T) { 1148 enum bool isStringSerializable = is(typeof(T.init.toString()) : string) 1149 && is(typeof(T.fromString("")) : T); 1150 } 1151 /// 1152 unittest { 1153 import std.conv; 1154 1155 // represented as a string when serialized 1156 static struct S { 1157 int value; 1158 1159 // dummy example implementations 1160 string toString() const { 1161 return value.to!string(); 1162 } 1163 1164 static S fromString(string s) { 1165 return S(s.to!int()); 1166 } 1167 } 1168 1169 static assert(isStringSerializable!S); 1170 } 1171 1172 /** Default policy (performs no customization). 1173 */ 1174 private template DefaultPolicy(T) { 1175 } 1176 1177 /** 1178 Checks if a given policy supports custom serialization for a given type. 1179 1180 A class or struct type is custom serializable according to a policy if 1181 the policy defines a pair of `toRepresentation`/`fromRepresentation` 1182 functions. Any class or struct type that has this trait for the policy supplied to 1183 `serializeWithPolicy` will be serialized by using the return value of the 1184 policy `toRepresentation` function instead of the original value. 1185 1186 This trait has precedence over `isCustomSerializable`, 1187 `isISOExtStringSerializable` and `isStringSerializable`. 1188 1189 See_Also: `dutils.data.utils.serialization.serializeWithPolicy` 1190 */ 1191 private template isPolicySerializable(alias Policy, T) { 1192 enum bool isPolicySerializable = is(typeof(Policy!T.toRepresentation(T.init))) 1193 && is(typeof(Policy!T.fromRepresentation(Policy!T.toRepresentation(T.init))) : T); 1194 } 1195 /// 1196 unittest { 1197 import std.conv; 1198 1199 // represented as the boxed value when serialized 1200 static struct Box(T) { 1201 T value; 1202 } 1203 1204 template BoxPol(S) { 1205 auto toRepresentation(S s) { 1206 return s.value; 1207 } 1208 1209 S fromRepresentation(typeof(S.init.value) v) { 1210 return S(v); 1211 } 1212 } 1213 1214 static assert(isPolicySerializable!(BoxPol, Box!int)); 1215 } 1216 1217 /** 1218 Chains serialization policy. 1219 1220 Constructs a serialization policy that given a type `T` will apply the 1221 first compatible policy `toRepresentation` and `fromRepresentation` 1222 functions. Policies are evaluated left-to-right according to 1223 `isPolicySerializable`. 1224 1225 See_Also: `dutils.data.utils.serialization.serializeWithPolicy` 1226 */ 1227 private template ChainedPolicy(alias Primary, Fallbacks...) { 1228 static if (Fallbacks.length == 0) { 1229 alias ChainedPolicy = Primary; 1230 } else { 1231 alias ChainedPolicy = ChainedPolicy!(ChainedPolicyImpl!(Primary, 1232 Fallbacks[0]), Fallbacks[1 .. $]); 1233 } 1234 } 1235 /// 1236 unittest { 1237 import std.conv; 1238 1239 // To be represented as the boxed value when serialized 1240 static struct Box(T) { 1241 T value; 1242 } 1243 // Also to berepresented as the boxed value when serialized, but has 1244 // a different way to access the value. 1245 static struct Box2(T) { 1246 private T v; 1247 ref T get() { 1248 return v; 1249 } 1250 } 1251 1252 template BoxPol(S) { 1253 auto toRepresentation(S s) { 1254 return s.value; 1255 } 1256 1257 S fromRepresentation(typeof(toRepresentation(S.init)) v) { 1258 return S(v); 1259 } 1260 } 1261 1262 template Box2Pol(S) { 1263 auto toRepresentation(S s) { 1264 return s.get(); 1265 } 1266 1267 S fromRepresentation(typeof(toRepresentation(S.init)) v) { 1268 S s; 1269 s.get() = v; 1270 return s; 1271 } 1272 } 1273 1274 alias ChainPol = ChainedPolicy!(BoxPol, Box2Pol); 1275 static assert(!isPolicySerializable!(BoxPol, Box2!int)); 1276 static assert(!isPolicySerializable!(Box2Pol, Box!int)); 1277 static assert(isPolicySerializable!(ChainPol, Box!int)); 1278 static assert(isPolicySerializable!(ChainPol, Box2!int)); 1279 } 1280 1281 private template ChainedPolicyImpl(alias Primary, alias Fallback) { 1282 template Pol(T) { 1283 static if (isPolicySerializable!(Primary, T)) { 1284 alias toRepresentation = Primary!T.toRepresentation; 1285 alias fromRepresentation = Primary!T.fromRepresentation; 1286 } else { 1287 alias toRepresentation = Fallback!T.toRepresentation; 1288 alias fromRepresentation = Fallback!T.fromRepresentation; 1289 } 1290 } 1291 1292 alias ChainedPolicyImpl = Pol; 1293 } 1294 1295 private template isBuiltinTuple(T, string member) { 1296 alias TM = AliasSeq!(typeof(__traits(getMember, T.init, member))); 1297 static if (TM.length > 1) 1298 enum isBuiltinTuple = true; 1299 else static if (is(typeof(__traits(getMember, T.init, member)) == TM[0])) 1300 enum isBuiltinTuple = false; 1301 else 1302 enum isBuiltinTuple = true; // single-element tuple 1303 } 1304 1305 // heuristically determines @safe'ty of the serializer by testing readValue and writeValue for type int 1306 private template isSafeSerializer(S) { 1307 alias T = Traits!(int, DefaultPolicy); 1308 static if (__traits(hasMember, S, "writeValue")) 1309 enum isSafeSerializer = __traits(compiles, (S s) @safe { 1310 s.writeValue!T(42); 1311 }); 1312 else 1313 static assert(0, "Serializer is missing required writeValue method"); 1314 } 1315 1316 // heuristically determines @safe'ty of the deserializer by testing readValue and writeValue for type int 1317 private template isSafeDeserializer(S) { 1318 alias T = Traits!(int, DefaultPolicy); 1319 static if (__traits(hasMember, S, "readValue")) 1320 enum isSafeDeserializer = __traits(compiles, (S s) @safe { 1321 s.readValue!(T, int)(); 1322 }); 1323 else 1324 static assert(0, "Deserializer is missing required readValue method"); 1325 } 1326 1327 private template hasAttribute(T, alias decl) { 1328 enum hasAttribute = findFirstUDA!(T, decl).found; 1329 } 1330 1331 unittest { 1332 @asArray int i1; 1333 static assert(hasAttribute!(AsArrayAttribute!DefaultPolicy, i1)); 1334 int i2; 1335 static assert(!hasAttribute!(AsArrayAttribute!DefaultPolicy, i2)); 1336 } 1337 1338 private template hasPolicyAttribute(alias T, alias POLICY, alias decl) { 1339 // __traits(identifier) to hack around T being a template and not a type 1340 // this if makes hasPolicyAttribute!(OptionalAttribute) == true when EmbedNullableIgnoreNullAttribute is present. 1341 static if (__traits(identifier, T) == __traits(identifier, OptionalAttribute)) 1342 enum hasPolicyAttribute = hasPolicyAttributeImpl!(T, POLICY, decl) 1343 || hasPolicyAttributeImpl!(EmbedNullableIgnoreNullAttribute, POLICY, decl); 1344 else 1345 enum hasPolicyAttribute = hasPolicyAttributeImpl!(T, POLICY, decl); 1346 } 1347 1348 private template hasPolicyAttributeImpl(alias T, alias POLICY, alias decl) { 1349 enum hasPolicyAttributeImpl = hasAttribute!(T!POLICY, decl) 1350 || hasAttribute!(T!DefaultPolicy, decl); 1351 } 1352 1353 unittest { 1354 import std.typecons : Nullable; 1355 1356 template CP(T) { 1357 } 1358 1359 @asArray!CP int i1; 1360 @asArray int i2; 1361 int i3; 1362 @embedNullable Nullable!int i4; 1363 1364 static assert(hasPolicyAttribute!(AsArrayAttribute, CP, i1)); 1365 static assert(hasPolicyAttribute!(AsArrayAttribute, CP, i2)); 1366 static assert(!hasPolicyAttribute!(AsArrayAttribute, CP, i3)); 1367 static assert(!hasPolicyAttribute!(AsArrayAttribute, DefaultPolicy, i1)); 1368 static assert(hasPolicyAttribute!(AsArrayAttribute, DefaultPolicy, i2)); 1369 static assert(!hasPolicyAttribute!(AsArrayAttribute, DefaultPolicy, i3)); 1370 static assert(hasPolicyAttribute!(EmbedNullableIgnoreNullAttribute, DefaultPolicy, i4)); 1371 static assert(hasPolicyAttribute!(OptionalAttribute, DefaultPolicy, i4)); 1372 static assert(!hasPolicyAttribute!(IgnoreAttribute, DefaultPolicy, i4)); 1373 } 1374 1375 private template hasAttributeL(T, ATTRIBUTES...) { 1376 static if (ATTRIBUTES.length == 1) { 1377 enum hasAttributeL = is(typeof(ATTRIBUTES[0]) == T); 1378 } else static if (ATTRIBUTES.length > 1) { 1379 enum hasAttributeL = hasAttributeL!(T, ATTRIBUTES[0 .. $ / 2]) 1380 || hasAttributeL!(T, ATTRIBUTES[$ / 2 .. $]); 1381 } else { 1382 enum hasAttributeL = false; 1383 } 1384 } 1385 1386 unittest { 1387 static assert(hasAttributeL!(AsArrayAttribute!DefaultPolicy, byName, asArray)); 1388 static assert(!hasAttributeL!(AsArrayAttribute!DefaultPolicy, byName)); 1389 } 1390 1391 private template hasPolicyAttributeL(alias T, alias POLICY, ATTRIBUTES...) { 1392 enum hasPolicyAttributeL = hasAttributeL!(T!POLICY, ATTRIBUTES) 1393 || hasAttributeL!(T!DefaultPolicy, ATTRIBUTES); 1394 } 1395 1396 private static T getAttribute(TT, string mname, T)(T default_value) { 1397 enum val = findFirstUDA!(T, __traits(getMember, TT, mname)); 1398 static if (val.found) 1399 return val.value; 1400 else 1401 return default_value; 1402 } 1403 1404 private static auto getPolicyAttribute(TT, string mname, alias Attribute, alias Policy)( 1405 Attribute!DefaultPolicy default_value) { 1406 enum val = findFirstUDA!(Attribute!Policy, TypeTuple!(__traits(getMember, TT, mname))[0]); 1407 static if (val.found) 1408 return val.value; 1409 else { 1410 enum val2 = findFirstUDA!(Attribute!DefaultPolicy, 1411 TypeTuple!(__traits(getMember, TT, mname))[0]); 1412 static if (val2.found) 1413 return val2.value; 1414 else 1415 return default_value; 1416 } 1417 } 1418 1419 private string underscoreStrip(string field_name) @safe nothrow @nogc { 1420 if (field_name.length < 1 || field_name[$ - 1] != '_') 1421 return field_name; 1422 else 1423 return field_name[0 .. $ - 1]; 1424 } 1425 1426 private template hasSerializableFields(T, alias POLICY, size_t idx = 0) { 1427 enum hasSerializableFields = SerializableFields!(T, POLICY).length > 0; 1428 /*static if (idx < __traits(allMembers, T).length) { 1429 enum mname = __traits(allMembers, T)[idx]; 1430 static if (!isRWPlainField!(T, mname) && !isRWField!(T, mname)) enum hasSerializableFields = hasSerializableFields!(T, idx+1); 1431 else static if (hasAttribute!(IgnoreAttribute, __traits(getMember, T, mname))) enum hasSerializableFields = hasSerializableFields!(T, idx+1); 1432 else enum hasSerializableFields = true; 1433 } else enum hasSerializableFields = false;*/ 1434 } 1435 1436 private template SerializableFields(COMPOSITE, alias POLICY) { 1437 alias SerializableFields = FilterSerializableFields!(COMPOSITE, POLICY, 1438 __traits(allMembers, COMPOSITE)); 1439 } 1440 1441 private template FilterSerializableFields(COMPOSITE, alias POLICY, FIELDS...) { 1442 static if (FIELDS.length > 1) { 1443 alias FilterSerializableFields = TypeTuple!(FilterSerializableFields!(COMPOSITE, POLICY, 1444 FIELDS[0 .. $ / 2]), FilterSerializableFields!(COMPOSITE, POLICY, FIELDS[$ / 2 .. $])); 1445 } else static if (FIELDS.length == 1) { 1446 alias T = COMPOSITE; 1447 enum mname = FIELDS[0]; 1448 static if (isRWPlainField!(T, mname) || isRWField!(T, mname)) { 1449 alias Tup = TypeTuple!(__traits(getMember, COMPOSITE, FIELDS[0])); 1450 static if (Tup.length != 1) { 1451 alias FilterSerializableFields = TypeTuple!(mname); 1452 } else { 1453 static if (!hasPolicyAttribute!(IgnoreAttribute, POLICY, __traits(getMember, T, mname))) { 1454 alias FilterSerializableFields = TypeTuple!(mname); 1455 } else 1456 alias FilterSerializableFields = TypeTuple!(); 1457 } 1458 } else 1459 alias FilterSerializableFields = TypeTuple!(); 1460 } else 1461 alias FilterSerializableFields = TypeTuple!(); 1462 } 1463 1464 private size_t getExpandedFieldCount(T, FIELDS...)() { 1465 size_t ret = 0; 1466 foreach (F; FIELDS) 1467 ret += TypeTuple!(__traits(getMember, T, F)).length; 1468 return ret; 1469 } 1470 1471 private template getExpandedFieldsData(T, FIELDS...) { 1472 import std.meta : aliasSeqOf, staticMap; 1473 import std.range : repeat, zip, iota; 1474 1475 enum subfieldsCount(alias F) = TypeTuple!(__traits(getMember, T, F)).length; 1476 alias processSubfield(alias F) = aliasSeqOf!(zip(repeat(F), iota(subfieldsCount!F))); 1477 alias getExpandedFieldsData = staticMap!(processSubfield, FIELDS); 1478 } 1479 1480 /******************************************************************************/ 1481 /* General serialization unit testing */ 1482 /******************************************************************************/ 1483 1484 version (unittest) { 1485 static assert(isSafeSerializer!TestSerializer); 1486 static assert(isSafeDeserializer!TestSerializer); 1487 1488 private struct TestSerializer { 1489 import std.array, std.conv, std.range, std.string, std.typecons; 1490 1491 string result; 1492 1493 enum isSupportedValueType(T) = is(T == string) || is(T == typeof(null)) 1494 || is(T == float) || is(T == int); 1495 1496 template unqualSeq(Specs...) { 1497 static if (Specs.length == 0) 1498 alias unqualSeq = AliasSeq!(); 1499 else static if (is(Specs[0])) 1500 alias unqualSeq = AliasSeq!(Unqual!(Specs[0]), unqualSeq!(Specs[1 .. $])); 1501 else 1502 alias unqualSeq = AliasSeq!(Specs[0], unqualSeq!(Specs[1 .. $])); 1503 } 1504 1505 template unqualType(T) { 1506 static if (isAssociativeArray!T) 1507 alias unqualType = Unqual!(ValueType!T)[Unqual!(KeyType!T)]; 1508 else static if (isTuple!T) 1509 alias unqualType = Tuple!(unqualSeq!(TemplateArgsOf!T)); 1510 else static if (isArray!T && !isSomeString!T) 1511 alias unqualType = Unqual!(ElementType!T)[]; 1512 else 1513 alias unqualType = Unqual!T; 1514 } 1515 1516 string getSerializedResult() @safe { 1517 return result; 1518 } 1519 1520 void beginWriteDictionary(Traits)() { 1521 result ~= "D(" ~ unqualType!(Traits.Type).mangleof ~ "){"; 1522 } 1523 1524 void endWriteDictionary(Traits)() { 1525 result ~= "}D(" ~ unqualType!(Traits.Type).mangleof ~ ")"; 1526 } 1527 1528 void beginWriteDictionaryEntry(Traits)(string name) { 1529 result ~= "DE(" ~ unqualType!(Traits.Type).mangleof ~ "," ~ name ~ ")("; 1530 } 1531 1532 void endWriteDictionaryEntry(Traits)(string name) { 1533 result ~= ")DE(" ~ unqualType!(Traits.Type).mangleof ~ "," ~ name ~ ")"; 1534 } 1535 1536 void beginWriteArray(Traits)(size_t length) { 1537 result ~= "A(" ~ unqualType!(Traits.Type).mangleof ~ ")[" ~ length.to!string ~ "]["; 1538 } 1539 1540 void endWriteArray(Traits)() { 1541 result ~= "]A(" ~ unqualType!(Traits.Type).mangleof ~ ")"; 1542 } 1543 1544 void beginWriteArrayEntry(Traits)(size_t i) { 1545 result ~= "AE(" ~ unqualType!(Traits.Type).mangleof ~ "," ~ i.to!string ~ ")("; 1546 } 1547 1548 void endWriteArrayEntry(Traits)(size_t i) { 1549 result ~= ")AE(" ~ unqualType!(Traits.Type).mangleof ~ "," ~ i.to!string ~ ")"; 1550 } 1551 1552 void writeValue(Traits, T)(T value) { 1553 if (is(T == typeof(null))) 1554 result ~= "null"; 1555 else { 1556 assert(isSupportedValueType!(unqualType!T)); 1557 result ~= "V(" ~ (unqualType!T).mangleof ~ ")(" ~ value.to!string ~ ")"; 1558 } 1559 } 1560 1561 // deserialization 1562 void readDictionary(Traits)(scope void delegate(string) @safe entry_callback) { 1563 skip("D(" ~ unqualType!(Traits.Type).mangleof ~ "){"); 1564 while (result.startsWith("DE(")) { 1565 result = result[3 .. $]; 1566 auto idx = result.indexOf(','); 1567 auto idx2 = result.indexOf(")("); 1568 assert(idx > 0 && idx2 > idx); 1569 auto t = result[0 .. idx]; 1570 auto n = result[idx + 1 .. idx2]; 1571 result = result[idx2 + 2 .. $]; 1572 entry_callback(n); 1573 skip(")DE(" ~ t ~ "," ~ n ~ ")"); 1574 } 1575 skip("}D(" ~ unqualType!(Traits.Type).mangleof ~ ")"); 1576 } 1577 1578 void beginReadDictionaryEntry(Traits)(string name) { 1579 } 1580 1581 void endReadDictionaryEntry(Traits)(string name) { 1582 } 1583 1584 void readArray(Traits)(scope void delegate(size_t) @safe size_callback, 1585 scope void delegate() @safe entry_callback) { 1586 skip("A(" ~ unqualType!(Traits.Type).mangleof ~ ")["); 1587 auto bidx = result.indexOf("]["); 1588 assert(bidx > 0); 1589 auto cnt = result[0 .. bidx].to!size_t; 1590 result = result[bidx + 2 .. $]; 1591 1592 size_t i = 0; 1593 while (result.startsWith("AE(")) { 1594 result = result[3 .. $]; 1595 auto idx = result.indexOf(','); 1596 auto idx2 = result.indexOf(")("); 1597 assert(idx > 0 && idx2 > idx); 1598 auto t = result[0 .. idx]; 1599 auto n = result[idx + 1 .. idx2]; 1600 result = result[idx2 + 2 .. $]; 1601 assert(n == i.to!string); 1602 entry_callback(); 1603 skip(")AE(" ~ t ~ "," ~ n ~ ")"); 1604 i++; 1605 } 1606 skip("]A(" ~ unqualType!(Traits.Type).mangleof ~ ")"); 1607 1608 assert(i == cnt); 1609 } 1610 1611 void beginReadArrayEntry(Traits)(size_t index) { 1612 } 1613 1614 void endReadArrayEntry(Traits)(size_t index) { 1615 } 1616 1617 T readValue(Traits, T)() { 1618 skip("V(" ~ unqualType!T.mangleof ~ ")("); 1619 auto idx = result.indexOf(')'); 1620 assert(idx >= 0); 1621 auto ret = result[0 .. idx].to!T; 1622 result = result[idx + 1 .. $]; 1623 return ret; 1624 } 1625 1626 void skip(string prefix) @safe { 1627 assert(result.startsWith(prefix), prefix ~ " vs. " ~ result); 1628 result = result[prefix.length .. $]; 1629 } 1630 1631 bool tryReadNull(Traits)() { 1632 if (result.startsWith("null")) { 1633 result = result[4 .. $]; 1634 return true; 1635 } else 1636 return false; 1637 } 1638 } 1639 } 1640 1641 unittest { // basic serialization behavior 1642 import std.typecons : Nullable; 1643 1644 static void test(T)(auto ref T value, string expected) { 1645 assert(serialize!TestSerializer(value) == expected, serialize!TestSerializer(value)); 1646 static if (isPointer!T) { 1647 if (value) 1648 assert(*deserialize!(TestSerializer, T)(expected) == *value); 1649 else 1650 assert(deserialize!(TestSerializer, T)(expected) is null); 1651 } else static if (is(T == Nullable!U, U)) { 1652 if (value.isNull()) 1653 assert(deserialize!(TestSerializer, T)(expected).isNull); 1654 else 1655 assert(deserialize!(TestSerializer, T)(expected) == value); 1656 } else 1657 assert(deserialize!(TestSerializer, T)(expected) == value); 1658 } 1659 1660 test("hello", "V(Aya)(hello)"); 1661 test(12, "V(i)(12)"); 1662 test(12.0, "V(Aya)(12)"); 1663 test(12.0f, "V(f)(12)"); 1664 assert(serialize!TestSerializer(null) == "null"); 1665 test(["hello", "world"], 1666 "A(AAya)[2][AE(Aya,0)(V(Aya)(hello))AE(Aya,0)AE(Aya,1)(V(Aya)(world))AE(Aya,1)]A(AAya)"); 1667 string mangleOfAA = (string[string]).mangleof; 1668 test(["hello": "world"], 1669 "D(" ~ mangleOfAA ~ "){DE(Aya,hello)(V(Aya)(world))DE(Aya,hello)}D(" ~ mangleOfAA ~ ")"); 1670 test(cast(int*) null, "null"); 1671 int i = 42; 1672 test(&i, "V(i)(42)"); 1673 Nullable!int j; 1674 test(j, "null"); 1675 j = 42; 1676 test(j, "V(i)(42)"); 1677 } 1678 1679 unittest { // basic user defined types 1680 static struct S { 1681 string f; 1682 } 1683 1684 enum Sm = S.mangleof; 1685 auto s = S("hello"); 1686 enum s_ser = "D(" ~ Sm ~ "){DE(Aya,f)(V(Aya)(hello))DE(Aya,f)}D(" ~ Sm ~ ")"; 1687 assert(serialize!TestSerializer(s) == s_ser, serialize!TestSerializer(s)); 1688 assert(deserialize!(TestSerializer, S)(s_ser) == s); 1689 1690 static class C { 1691 string f; 1692 } 1693 1694 enum Cm = C.mangleof; 1695 C c; 1696 assert(serialize!TestSerializer(c) == "null"); 1697 c = new C; 1698 c.f = "hello"; 1699 enum c_ser = "D(" ~ Cm ~ "){DE(Aya,f)(V(Aya)(hello))DE(Aya,f)}D(" ~ Cm ~ ")"; 1700 assert(serialize!TestSerializer(c) == c_ser); 1701 assert(deserialize!(TestSerializer, C)(c_ser).f == c.f); 1702 1703 enum E { 1704 hello, 1705 world 1706 } 1707 1708 assert(serialize!TestSerializer(E.hello) == "V(i)(0)"); 1709 assert(serialize!TestSerializer(E.world) == "V(i)(1)"); 1710 } 1711 1712 unittest { // tuple serialization 1713 import std.typecons : Tuple; 1714 1715 static struct S(T...) { 1716 T f; 1717 } 1718 1719 enum Sm = S!(int, string).mangleof; 1720 enum Tum = Tuple!(int, string).mangleof; 1721 const s = S!(int, string)(42, "hello"); 1722 1723 const ss = serialize!TestSerializer(s); 1724 const es = "D(" ~ Sm ~ "){DE(" ~ Tum ~ ",f)(A(" ~ Tum 1725 ~ ")[2][AE(i,0)(V(i)(42))AE(i,0)AE(Aya,1)(V(Aya)(hello))AE(Aya,1)]A(" ~ Tum 1726 ~ "))DE(" ~ Tum ~ ",f)}D(" ~ Sm ~ ")"; 1727 assert(ss == es); 1728 1729 const dss = deserialize!(TestSerializer, typeof(s))(ss); 1730 assert(dss == s); 1731 1732 static struct T { 1733 @asArray S!(int, string) g; 1734 } 1735 1736 enum Tm = T.mangleof; 1737 const t = T(s); 1738 1739 const st = serialize!TestSerializer(t); 1740 const et = "D(" ~ Tm ~ "){DE(" ~ Sm ~ ",g)(A(" ~ Sm 1741 ~ ")[2][AE(i,0)(V(i)(42))AE(i,0)AE(Aya,1)(V(Aya)(hello))AE(Aya,1)]A(" ~ Sm 1742 ~ "))DE(" ~ Sm ~ ",g)}D(" ~ Tm ~ ")"; 1743 assert(st == et); 1744 1745 const dst = deserialize!(TestSerializer, typeof(t))(st); 1746 assert(dst == t); 1747 } 1748 1749 unittest { // named tuple serialization 1750 import std.typecons : tuple; 1751 1752 static struct I { 1753 int i; 1754 } 1755 1756 static struct S { 1757 int x; 1758 string s_; 1759 } 1760 1761 static struct T { 1762 @asArray typeof(tuple!(FieldNameTuple!I)(I.init.tupleof)) tuple1AsArray; 1763 1764 @name(fullyQualifiedName!I) 1765 typeof(tuple!(FieldNameTuple!I)(I.init.tupleof)) tuple1AsDictionary; 1766 1767 @asArray typeof(tuple!(FieldNameTuple!S)(S.init.tupleof)) tuple2AsArray; 1768 1769 @name(fullyQualifiedName!S) 1770 typeof(tuple!(FieldNameTuple!S)(S.init.tupleof)) tuple2AsDictionary; 1771 } 1772 1773 const i = I(42); 1774 const s = S(42, "hello"); 1775 const T t = {i.tupleof, i.tupleof, s.tupleof, s.tupleof}; 1776 1777 const st = serialize!TestSerializer(t); 1778 1779 enum Tm = T.mangleof; 1780 enum TuIm = typeof(T.tuple1AsArray).mangleof; 1781 enum TuSm = typeof(T.tuple2AsArray).mangleof; 1782 1783 const et = "D(" ~ Tm ~ ")" ~ "{" ~ "DE(" ~ TuIm ~ ",tuple1AsArray)" ~ "(" ~ "V(i)(42)" 1784 ~ ")" ~ "DE(" ~ TuIm ~ ",tuple1AsArray)" ~ "DE(" ~ TuIm ~ "," 1785 ~ fullyQualifiedName!I ~ ")" ~ "(" ~ "D(" ~ TuIm ~ ")" ~ "{" ~ "DE(i,i)" ~ "(" 1786 ~ "V(i)(42)" ~ ")" ~ "DE(i,i)" ~ "}" ~ "D(" ~ TuIm ~ ")" ~ ")" ~ "DE(" ~ TuIm ~ "," 1787 ~ fullyQualifiedName!I ~ ")" ~ "DE(" ~ TuSm ~ ",tuple2AsArray)" ~ "(" ~ "A(" ~ TuSm 1788 ~ ")[2]" ~ "[" ~ "AE(i,0)" ~ "(" ~ "V(i)(42)" ~ ")" ~ "AE(i,0)" ~ "AE(Aya,1)" 1789 ~ "(" ~ "V(Aya)(hello)" ~ ")" ~ "AE(Aya,1)" ~ "]" ~ "A(" ~ TuSm ~ ")" ~ ")" ~ "DE(" 1790 ~ TuSm ~ ",tuple2AsArray)" ~ "DE(" ~ TuSm ~ "," ~ fullyQualifiedName!S ~ ")" 1791 ~ "(" ~ "D(" ~ TuSm ~ ")" ~ "{" ~ "DE(i,x)" ~ "(" ~ "V(i)(42)" ~ ")" ~ "DE(i,x)" 1792 ~ "DE(Aya,s)" ~ "(" ~ "V(Aya)(hello)" ~ ")" ~ "DE(Aya,s)" ~ "}" ~ "D(" ~ TuSm 1793 ~ ")" ~ ")" ~ "DE(" ~ TuSm ~ "," ~ fullyQualifiedName!S ~ ")" ~ "}" ~ "D(" ~ Tm ~ ")"; 1794 assert(st == et); 1795 1796 const dst = deserialize!(TestSerializer, typeof(t))(st); 1797 assert(dst == t); 1798 } 1799 1800 unittest { // testing the various UDAs 1801 enum E { 1802 hello, 1803 world 1804 } 1805 1806 enum Em = E.mangleof; 1807 static struct S { 1808 @byName E e; 1809 @ignore int i; 1810 @optional float f; 1811 } 1812 1813 enum Sm = S.mangleof; 1814 auto s = S(E.world, 42, 1.0f); 1815 assert(serialize!TestSerializer(s) == "D(" ~ Sm ~ "){DE(" ~ Em 1816 ~ ",e)(V(Aya)(world))DE(" ~ Em ~ ",e)DE(f,f)(V(f)(1))DE(f,f)}D(" ~ Sm ~ ")"); 1817 } 1818 1819 unittest { // custom serialization support 1820 // iso-ext 1821 import std.datetime; 1822 1823 auto t = TimeOfDay(6, 31, 23); 1824 assert(serialize!TestSerializer(t) == "V(Aya)(06:31:23)"); 1825 auto d = Date(1964, 1, 23); 1826 assert(serialize!TestSerializer(d) == "V(Aya)(1964-01-23)"); 1827 auto dt = DateTime(d, t); 1828 assert(serialize!TestSerializer(dt) == "V(Aya)(1964-01-23T06:31:23)"); 1829 auto st = SysTime(dt, UTC()); 1830 assert(serialize!TestSerializer(st) == "V(Aya)(1964-01-23T06:31:23Z)"); 1831 } 1832 1833 @safe unittest { // custom serialization support 1834 // string 1835 static struct S1 { 1836 int i; 1837 string toString() const @safe { 1838 return "hello"; 1839 } 1840 1841 static S1 fromString(string) @safe { 1842 return S1.init; 1843 } 1844 } 1845 1846 static struct S2 { 1847 int i; 1848 string toString() const { 1849 return "hello"; 1850 } 1851 } 1852 1853 enum S2m = S2.mangleof; 1854 static struct S3 { 1855 int i; 1856 static S3 fromString(string) { 1857 return S3.init; 1858 } 1859 } 1860 1861 enum S3m = S3.mangleof; 1862 assert(serialize!TestSerializer(S1.init) == "V(Aya)(hello)"); 1863 assert(serialize!TestSerializer(S2.init) == "D(" ~ S2m ~ "){DE(i,i)(V(i)(0))DE(i,i)}D(" ~ S2m 1864 ~ ")"); 1865 assert(serialize!TestSerializer(S3.init) == "D(" ~ S3m ~ "){DE(i,i)(V(i)(0))DE(i,i)}D(" ~ S3m 1866 ~ ")"); 1867 1868 // custom 1869 static struct C1 { 1870 int i; 1871 float toRepresentation() const @safe { 1872 return 1.0f; 1873 } 1874 1875 static C1 fromRepresentation(float f) @safe { 1876 return C1.init; 1877 } 1878 } 1879 1880 static struct C2 { 1881 int i; 1882 float toRepresentation() const { 1883 return 1.0f; 1884 } 1885 } 1886 1887 enum C2m = C2.mangleof; 1888 static struct C3 { 1889 int i; 1890 static C3 fromRepresentation(float f) { 1891 return C3.init; 1892 } 1893 } 1894 1895 enum C3m = C3.mangleof; 1896 assert(serialize!TestSerializer(C1.init) == "V(f)(1)"); 1897 assert(serialize!TestSerializer(C2.init) == "D(" ~ C2m ~ "){DE(i,i)(V(i)(0))DE(i,i)}D(" ~ C2m 1898 ~ ")"); 1899 assert(serialize!TestSerializer(C3.init) == "D(" ~ C3m ~ "){DE(i,i)(V(i)(0))DE(i,i)}D(" ~ C3m 1900 ~ ")"); 1901 } 1902 1903 unittest // Testing corner case: member function returning by ref 1904 { 1905 import dutils.data.json; 1906 1907 static struct S { 1908 int i; 1909 ref int foo() { 1910 return i; 1911 } 1912 } 1913 1914 static assert(__traits(compiles, { S().serializeToJSON(); })); 1915 static assert(__traits(compiles, { JSON().deserializeJSON!S(); })); 1916 1917 auto s = S(1); 1918 assert(s.serializeToJSON().deserializeJSON!S() == s); 1919 } 1920 1921 unittest // Testing corner case: Variadic template constructors and methods 1922 { 1923 import dutils.data.json; 1924 1925 static struct S { 1926 int i; 1927 this(Args...)(Args args) { 1928 } 1929 1930 int foo(Args...)(Args args) { 1931 return i; 1932 } 1933 1934 ref int bar(Args...)(Args args) { 1935 return i; 1936 } 1937 } 1938 1939 static assert(__traits(compiles, { S().serializeToJSON(); })); 1940 static assert(__traits(compiles, { JSON().deserializeJSON!S(); })); 1941 1942 auto s = S(1); 1943 assert(s.serializeToJSON().deserializeJSON!S() == s); 1944 } 1945 1946 @safe unittest // Make sure serializing through properties still works 1947 { 1948 import dutils.data.json; 1949 1950 static struct S { 1951 @safe: 1952 public int i; 1953 private int privateJ; 1954 1955 @property int j() @safe { 1956 return privateJ; 1957 } 1958 1959 @property void j(int j) @safe { 1960 privateJ = j; 1961 } 1962 } 1963 1964 auto s = S(1, 2); 1965 assert(s.serializeToJSON().deserializeJSON!S() == s); 1966 } 1967 1968 @safe unittest // Immutable data deserialization 1969 { 1970 import dutils.data.json; 1971 1972 static struct S { 1973 int a; 1974 } 1975 1976 static class C { 1977 immutable(S)[] arr; 1978 } 1979 1980 auto c = new C; 1981 c.arr ~= S(10); 1982 auto d = c.serializeToJSON().deserializeJSON!(immutable C); 1983 static assert(is(typeof(d) == immutable C)); 1984 assert(d.arr == c.arr); 1985 } 1986 1987 unittest { // test BitFlags serialization 1988 import std.typecons : BitFlags; 1989 1990 enum Flag { 1991 a = 1 << 0, 1992 b = 1 << 1, 1993 c = 1 << 2 1994 } 1995 1996 enum Flagm = Flag.mangleof; 1997 1998 alias Flags = BitFlags!Flag; 1999 enum Flagsm = Flags.mangleof; 2000 2001 enum Fi_ser = "A(" ~ Flagsm ~ ")[0][]A(" ~ Flagsm ~ ")"; 2002 assert(serialize!TestSerializer(Flags.init) == Fi_ser); 2003 2004 enum Fac_ser = "A(" ~ Flagsm ~ ")[2][AE(" ~ Flagm ~ ",0)(V(i)(1))AE(" ~ Flagm 2005 ~ ",0)AE(" ~ Flagm ~ ",1)(V(i)(4))AE(" ~ Flagm ~ ",1)]A(" ~ Flagsm ~ ")"; 2006 assert(serialize!TestSerializer(Flags(Flag.a, Flag.c)) == Fac_ser); 2007 2008 struct S { 2009 @byName Flags f; 2010 } 2011 2012 enum Sm = S.mangleof; 2013 enum Sac_ser = "D(" ~ Sm ~ "){DE(" ~ Flagsm ~ ",f)(A(" ~ Flagsm ~ ")[2][AE(" ~ Flagm ~ ",0)(V(Aya)(a))AE(" 2014 ~ Flagm ~ ",0)AE(" ~ Flagm ~ ",1)(V(Aya)(c))AE(" ~ Flagm ~ ",1)]A(" ~ Flagsm 2015 ~ "))DE(" ~ Flagsm ~ ",f)}D(" ~ Sm ~ ")"; 2016 2017 assert(serialize!TestSerializer(S(Flags(Flag.a, Flag.c))) == Sac_ser); 2018 2019 assert(deserialize!(TestSerializer, Flags)(Fi_ser) == Flags.init); 2020 assert(deserialize!(TestSerializer, Flags)(Fac_ser) == Flags(Flag.a, Flag.c)); 2021 assert(deserialize!(TestSerializer, S)(Sac_ser) == S(Flags(Flag.a, Flag.c))); 2022 } 2023 2024 @safe unittest { // issue #1182 2025 struct T { 2026 int x; 2027 string y; 2028 } 2029 2030 struct S { 2031 @asArray T t; 2032 } 2033 2034 auto s = S(T(42, "foo")); 2035 enum Sm = S.mangleof; 2036 enum Tm = T.mangleof; 2037 enum s_ser = "D(" ~ Sm ~ "){DE(" ~ Tm ~ ",t)(A(" ~ Tm 2038 ~ ")[2][AE(i,0)(V(i)(42))AE(i,0)AE(Aya,1)(V(Aya)(foo))AE(Aya,1)]A(" ~ Tm 2039 ~ "))DE(" ~ Tm ~ ",t)}D(" ~ Sm ~ ")"; 2040 2041 auto serialized = serialize!TestSerializer(s); 2042 assert(serialized == s_ser, serialized); 2043 assert(deserialize!(TestSerializer, S)(serialized) == s); 2044 } 2045 2046 @safe unittest { // issue #1352 - ingore per policy 2047 struct P1 { 2048 } 2049 2050 struct P2 { 2051 } 2052 2053 struct T { 2054 @ignore int a = 5; 2055 @ignore!P1 @ignore!P2 int b = 6; 2056 @ignore!P1 c = 7; 2057 int d = 8; 2058 } 2059 2060 auto t = T(1, 2, 3, 4); 2061 auto Tm = T.mangleof; 2062 auto t_ser_plain = "D(" ~ Tm 2063 ~ "){DE(i,b)(V(i)(2))DE(i,b)DE(i,c)(V(i)(3))DE(i,c)DE(i,d)(V(i)(4))DE(i,d)}D(" ~ Tm ~ ")"; 2064 auto t_ser_p1 = "D(" ~ Tm ~ "){DE(i,d)(V(i)(4))DE(i,d)}D(" ~ Tm ~ ")"; 2065 auto t_ser_p2 = "D(" ~ Tm ~ "){DE(i,c)(V(i)(3))DE(i,c)DE(i,d)(V(i)(4))DE(i,d)}D(" ~ Tm ~ ")"; 2066 2067 { 2068 auto serialized_plain = serialize!TestSerializer(t); 2069 assert(serialized_plain == t_ser_plain); 2070 assert(deserialize!(TestSerializer, T)(serialized_plain) == T(5, 2, 3, 4)); 2071 } 2072 2073 { 2074 auto serialized_p1 = serializeWithPolicy!(TestSerializer, P1)(t); 2075 assert(serialized_p1 == t_ser_p1, serialized_p1); 2076 assert(deserializeWithPolicy!(TestSerializer, P1, T)(serialized_p1) == T(5, 6, 7, 4)); 2077 } 2078 2079 { 2080 auto serialized_p2 = serializeWithPolicy!(TestSerializer, P2)(t); 2081 assert(serialized_p2 == t_ser_p2); 2082 assert(deserializeWithPolicy!(TestSerializer, P2, T)(serialized_p2) == T(5, 6, 3, 4)); 2083 } 2084 } 2085 2086 unittest { 2087 import std.conv : to; 2088 import std.string : toLower, toUpper; 2089 2090 template P(T) if (is(T == enum)) { 2091 @safe: 2092 static string toRepresentation(T v) { 2093 return v.to!string.toLower(); 2094 } 2095 2096 static T fromRepresentation(string str) { 2097 return str.toUpper().to!T; 2098 } 2099 } 2100 2101 enum E { 2102 RED, 2103 GREEN 2104 } 2105 2106 assert(P!E.fromRepresentation("green") == E.GREEN); 2107 static assert(isPolicySerializable!(P, E)); 2108 2109 auto ser_red = "V(Aya)(red)"; 2110 assert(serializeWithPolicy!(TestSerializer, P)(E.RED) == ser_red, 2111 serializeWithPolicy!(TestSerializer, P)(E.RED)); 2112 assert(deserializeWithPolicy!(TestSerializer, P, E)(ser_red) == E.RED); 2113 2114 import dutils.data.json : JSON, JSONSerializer; 2115 2116 assert(serializeWithPolicy!(JSONSerializer, P)(E.RED) == JSON("red")); 2117 } 2118 2119 unittest { 2120 static struct R { 2121 int y; 2122 } 2123 2124 static struct Custom { 2125 @safe: 2126 int x; 2127 R toRepresentation() const { 2128 return R(x); 2129 } 2130 2131 static Custom fromRepresentation(R r) { 2132 return Custom(r.y); 2133 } 2134 } 2135 2136 auto c = Custom(42); 2137 auto Rn = R.mangleof; 2138 auto ser = serialize!TestSerializer(c); 2139 assert(ser == "D(" ~ Rn ~ "){DE(i,y)(V(i)(42))DE(i,y)}D(" ~ Rn ~ ")"); 2140 auto deser = deserialize!(TestSerializer, Custom)(ser); 2141 assert(deser.x == 42); 2142 } 2143 2144 unittest { 2145 import std.typecons : Typedef; 2146 2147 alias T = Typedef!int; 2148 auto ser = serialize!TestSerializer(T(42)); 2149 assert(ser == "V(i)(42)", ser); 2150 auto deser = deserialize!(TestSerializer, T)(ser); 2151 assert(deser == 42); 2152 } 2153 2154 @safe unittest { 2155 static struct Foo { 2156 Foo[] foos; 2157 } 2158 2159 Foo f; 2160 string ser = serialize!TestSerializer(f); 2161 assert(deserialize!(TestSerializer, Foo)(ser) == f); 2162 } 2163 2164 @system unittest { 2165 static struct SystemSerializer { 2166 TestSerializer ser; 2167 alias ser this; 2168 this(string s) { 2169 ser.result = s; 2170 } 2171 2172 T readValue(Traits, T)() @system { 2173 return ser.readValue!(Traits, T); 2174 } 2175 2176 void writeValue(Traits, T)(T value) @system { 2177 ser.writeValue!(Traits, T)(value); 2178 } 2179 2180 void readDictionary(Traits)(scope void delegate(string) @system entry_callback) { 2181 ser.readDictionary!Traits((s) @trusted { entry_callback(s); }); 2182 } 2183 2184 void readArray(Traits)(scope void delegate(size_t) @system size_callback, 2185 scope void delegate() @system entry_callback) { 2186 ser.readArray!Traits((s) @trusted { size_callback(s); }, () @trusted { 2187 entry_callback(); 2188 }); 2189 } 2190 } 2191 2192 static struct Bar { 2193 Bar[] foos; 2194 int i; 2195 } 2196 2197 Bar f; 2198 string ser = serialize!SystemSerializer(f); 2199 assert(deserialize!(SystemSerializer, Bar)(ser) == f); 2200 } 2201 2202 @safe unittest { 2203 static struct S { 2204 @name("+foo") int bar; 2205 } 2206 2207 auto Sn = S.mangleof; 2208 auto s = S(42); 2209 string ser = serialize!TestSerializer(s); 2210 assert(ser == "D(" ~ Sn ~ "){DE(i,+foo)(V(i)(42))DE(i,+foo)}D(" ~ Sn ~ ")", ser); 2211 auto deser = deserialize!(TestSerializer, S)(ser); 2212 assert(deser.bar == 42); 2213 } 2214 2215 @safe unittest { 2216 static struct S { 2217 int bar_; 2218 } 2219 2220 auto Sn = S.mangleof; 2221 auto s = S(42); 2222 string ser = serialize!TestSerializer(s); 2223 assert(ser == "D(" ~ Sn ~ "){DE(i,bar)(V(i)(42))DE(i,bar)}D(" ~ Sn ~ ")", ser); 2224 auto deser = deserialize!(TestSerializer, S)(ser); 2225 assert(deser.bar_ == 42); 2226 } 2227 2228 @safe unittest { // issue 1941 2229 static struct Bar { 2230 Bar[] foos; 2231 int i; 2232 } 2233 2234 Bar b1 = {[{null, 2}], 1}; 2235 auto s = serialize!TestSerializer(b1); 2236 auto b = deserialize!(TestSerializer, Bar)(s); 2237 assert(b.i == 1); 2238 assert(b.foos.length == 1); 2239 assert(b.foos[0].i == 2); 2240 } 2241 2242 unittest { // issue 1991 - @system property getters/setters does not compile 2243 static class A { 2244 @property @name("foo") { 2245 @safe string fooString() const { 2246 return "a"; 2247 } 2248 2249 @safe void fooString(string a) { 2250 } 2251 } 2252 } 2253 2254 auto a1 = new A; 2255 auto b = serialize!TestSerializer(a1); 2256 auto a2 = deserialize!(TestSerializer, A)(b); 2257 } 2258 2259 unittest { // issue #2110 - single-element tuples 2260 static struct F { 2261 int field; 2262 } 2263 2264 { 2265 static struct S { 2266 typeof(F.init.tupleof) fields; 2267 } 2268 2269 auto b = serialize!TestSerializer(S(42)); 2270 auto a = deserialize!(TestSerializer, S)(b); 2271 assert(a.fields[0] == 42); 2272 } 2273 2274 { 2275 static struct T { 2276 @asArray typeof(F.init.tupleof) fields; 2277 } 2278 2279 auto b = serialize!TestSerializer(T(42)); 2280 auto a = deserialize!(TestSerializer, T)(b); 2281 assert(a.fields[0] == 42); 2282 } 2283 } 2284 2285 @safe unittest { 2286 import std.typecons : Nullable; 2287 2288 struct S { 2289 @embedNullable Nullable!int x; 2290 @embedNullable Nullable!string s; 2291 } 2292 2293 enum Sn = S.mangleof; 2294 2295 auto s = S(Nullable!int(3), Nullable!string.init); 2296 auto expected = "D(" ~ Sn ~ "){DE(i,x)(V(i)(3))DE(i,x)}D(" ~ Sn ~ ")"; 2297 2298 assert(serialize!TestSerializer(s) == expected, serialize!TestSerializer(s)); 2299 assert(deserialize!(TestSerializer, S)(expected) == s); 2300 2301 s.s = "hello"; 2302 expected = "D(" ~ Sn ~ "){DE(i,x)(V(i)(3))DE(i,x)DE(Aya,s)(V(Aya)(hello))DE(Aya,s)}D(" ~ Sn ~ ")"; 2303 assert(serialize!TestSerializer(s) == expected, serialize!TestSerializer(s)); 2304 assert(deserialize!(TestSerializer, S)(expected) == s); 2305 2306 s.x.nullify(); 2307 expected = "D(" ~ Sn ~ "){DE(Aya,s)(V(Aya)(hello))DE(Aya,s)}D(" ~ Sn ~ ")"; 2308 assert(serialize!TestSerializer(s) == expected); 2309 assert(deserialize!(TestSerializer, S)(expected) == s); 2310 2311 s.s.nullify(); 2312 expected = "D(" ~ Sn ~ "){}D(" ~ Sn ~ ")"; 2313 assert(serialize!TestSerializer(s) == expected); 2314 assert(deserialize!(TestSerializer, S)(expected) == s); 2315 }