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)(); 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 vibe.data.serialization; 124 125 import vibe.internal.meta.traits; 126 import vibe.internal.meta.uda; 127 128 import std.array : Appender, appender; 129 import std.conv : to; 130 import std.exception : enforce; 131 import std.traits; 132 import std.typetuple; 133 134 135 /** 136 Serializes a value with the given serializer. 137 138 The serializer must have a value result for the first form 139 to work. Otherwise, use the range based form. 140 141 See_Also: `vibe.data.json.JsonSerializer`, `vibe.data.json.JsonStringSerializer`, `vibe.data.bson.BsonSerializer` 142 */ 143 auto serialize(Serializer, T, ARGS...)(T value, ARGS args) 144 { 145 auto serializer = Serializer(args); 146 serialize(serializer, value); 147 return serializer.getSerializedResult(); 148 } 149 /// ditto 150 void serialize(Serializer, T)(ref Serializer serializer, T value) 151 { 152 serializeWithPolicy!(Serializer, DefaultPolicy)(serializer, value); 153 } 154 155 /** Note that there is a convenience function `vibe.data.json.serializeToJson` 156 that can be used instead of manually invoking `serialize`. 157 */ 158 unittest { 159 import vibe.data.json; 160 161 struct Test { 162 int value; 163 string text; 164 } 165 166 Test test; 167 test.value = 12; 168 test.text = "Hello"; 169 170 Json serialized = serialize!JsonSerializer(test); 171 assert(serialized["value"].get!int == 12); 172 assert(serialized["text"].get!string == "Hello"); 173 } 174 175 unittest { 176 import vibe.data.json; 177 178 // Make sure that immutable(char[]) works just like string 179 // (i.e., immutable(char)[]). 180 immutable key = "answer"; 181 auto ints = [key: 42]; 182 auto serialized = serialize!JsonSerializer(ints); 183 assert(serialized[key].get!int == 42); 184 } 185 186 /** 187 Serializes a value with the given serializer, representing values according to `Policy` when possible. 188 189 The serializer must have a value result for the first form 190 to work. Otherwise, use the range based form. 191 192 See_Also: `vibe.data.json.JsonSerializer`, `vibe.data.json.JsonStringSerializer`, `vibe.data.bson.BsonSerializer` 193 */ 194 auto serializeWithPolicy(Serializer, alias Policy, T, ARGS...)(T value, ARGS args) 195 { 196 auto serializer = Serializer(args); 197 serializeWithPolicy!(Serializer, Policy)(serializer, value); 198 return serializer.getSerializedResult(); 199 } 200 /// ditto 201 void serializeWithPolicy(Serializer, alias Policy, T)(ref Serializer serializer, T value) 202 { 203 static if (is(typeof(serializer.beginWriteDocument!T()))) 204 serializer.beginWriteDocument!T(); 205 serializeValueImpl!(Serializer, Policy).serializeValue!T(serializer, value); 206 static if (is(typeof(serializer.endWriteDocument!T()))) 207 serializer.endWriteDocument!T(); 208 } 209 /// 210 version (unittest) 211 { 212 } 213 214 /// 215 unittest { 216 import vibe.data.json; 217 218 template SizePol(T) 219 if (__traits(allMembers, T) == TypeTuple!("x", "y")) 220 { 221 import std.conv; 222 import std.array; 223 224 static string toRepresentation(T value) @safe { 225 return to!string(value.x) ~ "x" ~ to!string(value.y); 226 } 227 228 static T fromRepresentation(string value) { 229 string[] fields = value.split('x'); 230 alias fieldT = typeof(T.x); 231 auto x = to!fieldT(fields[0]); 232 auto y = to!fieldT(fields[1]); 233 return T(x, y); 234 } 235 } 236 237 static struct SizeI { 238 int x; 239 int y; 240 } 241 SizeI sizeI = SizeI(1,2); 242 Json serializedI = serializeWithPolicy!(JsonSerializer, SizePol)(sizeI); 243 assert(serializedI.get!string == "1x2"); 244 245 static struct SizeF { 246 float x; 247 float y; 248 } 249 SizeF sizeF = SizeF(0.1f,0.2f); 250 Json serializedF = serializeWithPolicy!(JsonSerializer, SizePol)(sizeF); 251 assert(serializedF.get!string == "0.1x0.2"); 252 } 253 254 255 /** 256 Deserializes and returns a serialized value. 257 258 serialized_data can be either an input range or a value containing 259 the serialized data, depending on the type of serializer used. 260 261 See_Also: `vibe.data.json.JsonSerializer`, `vibe.data.json.JsonStringSerializer`, `vibe.data.bson.BsonSerializer` 262 */ 263 T deserialize(Serializer, T, ARGS...)(ARGS args) 264 { 265 return deserializeWithPolicy!(Serializer, DefaultPolicy, T)(args); 266 } 267 268 /** Note that there is a convenience function `vibe.data.json.deserializeJson` 269 that can be used instead of manually invoking `deserialize`. 270 */ 271 unittest { 272 import vibe.data.json; 273 274 struct Test { 275 int value; 276 string text; 277 } 278 279 Json serialized = Json.emptyObject; 280 serialized["value"] = 12; 281 serialized["text"] = "Hello"; 282 283 Test test = deserialize!(JsonSerializer, Test)(serialized); 284 assert(test.value == 12); 285 assert(test.text == "Hello"); 286 } 287 288 /** 289 Deserializes and returns a serialized value, interpreting values according to `Policy` when possible. 290 291 serialized_data can be either an input range or a value containing 292 the serialized data, depending on the type of serializer used. 293 294 See_Also: `vibe.data.json.JsonSerializer`, `vibe.data.json.JsonStringSerializer`, `vibe.data.bson.BsonSerializer` 295 */ 296 T deserializeWithPolicy(Serializer, alias Policy, T, ARGS...)(ARGS args) 297 { 298 auto deserializer = Serializer(args); 299 return deserializeValueImpl!(Serializer, Policy).deserializeValue!T(deserializer); 300 } 301 302 /// 303 unittest { 304 import vibe.data.json; 305 306 template SizePol(T) 307 if (__traits(allMembers, T) == TypeTuple!("x", "y")) 308 { 309 import std.conv; 310 import std.array; 311 312 static string toRepresentation(T value) 313 @safe { 314 return to!string(value.x) ~ "x" ~ to!string(value.y); 315 } 316 317 static T fromRepresentation(string value) 318 @safe { 319 string[] fields = value.split('x'); 320 alias fieldT = typeof(T.x); 321 auto x = to!fieldT(fields[0]); 322 auto y = to!fieldT(fields[1]); 323 return T(x, y); 324 } 325 } 326 327 static struct SizeI { 328 int x; 329 int y; 330 } 331 332 Json serializedI = "1x2"; 333 SizeI sizeI = deserializeWithPolicy!(JsonSerializer, SizePol, SizeI)(serializedI); 334 assert(sizeI.x == 1); 335 assert(sizeI.y == 2); 336 337 static struct SizeF { 338 float x; 339 float y; 340 } 341 Json serializedF = "0.1x0.2"; 342 SizeF sizeF = deserializeWithPolicy!(JsonSerializer, SizePol, SizeF)(serializedF); 343 assert(sizeF.x == 0.1f); 344 assert(sizeF.y == 0.2f); 345 } 346 347 private template serializeValueImpl(Serializer, alias Policy) { 348 alias _Policy = Policy; 349 static assert(Serializer.isSupportedValueType!string, "All serializers must support string values."); 350 static assert(Serializer.isSupportedValueType!(typeof(null)), "All serializers must support null values."); 351 352 // work around https://issues.dlang.org/show_bug.cgi?id=16528 353 static if (isSafeSerializer!Serializer) { 354 void serializeValue(T, ATTRIBUTES...)(ref Serializer ser, T value) @safe { serializeValueDeduced!(T, ATTRIBUTES)(ser, value); } 355 } else { 356 void serializeValue(T, ATTRIBUTES...)(ref Serializer ser, T value) { serializeValueDeduced!(T, ATTRIBUTES)(ser, value); } 357 } 358 359 private void serializeValueDeduced(T, ATTRIBUTES...)(ref Serializer ser, T value) 360 { 361 import std.typecons : BitFlags, Nullable, Tuple, Typedef, TypedefType, tuple; 362 363 alias TU = Unqual!T; 364 365 alias Traits = .Traits!(TU, _Policy, ATTRIBUTES); 366 367 static if (isPolicySerializable!(Policy, TU)) { 368 alias CustomType = typeof(Policy!TU.toRepresentation(TU.init)); 369 ser.serializeValue!(CustomType, ATTRIBUTES)(Policy!TU.toRepresentation(value)); 370 } else static if (is(TU == enum)) { 371 static if (hasPolicyAttributeL!(ByNameAttribute, Policy, ATTRIBUTES)) { 372 ser.serializeValue!(string)(value.to!string()); 373 } else { 374 ser.serializeValue!(OriginalType!TU)(cast(OriginalType!TU)value); 375 } 376 } else static if (Serializer.isSupportedValueType!TU) { 377 static if (is(TU == typeof(null))) ser.writeValue!Traits(null); 378 else ser.writeValue!(Traits, TU)(value); 379 } else static if (/*isInstanceOf!(Tuple, TU)*/is(T == Tuple!TPS, TPS...)) { 380 import std.algorithm.searching: all; 381 static if (all!"!a.empty"([TU.fieldNames]) && 382 !hasPolicyAttributeL!(AsArrayAttribute, Policy, ATTRIBUTES)) { 383 static if (__traits(compiles, ser.beginWriteDictionary!TU(0))) { 384 auto nfields = value.length; 385 ser.beginWriteDictionary!Traits(nfields); 386 } else { 387 ser.beginWriteDictionary!Traits(); 388 } 389 foreach (i, TV; TU.Types) { 390 alias STraits = SubTraits!(Traits, TV); 391 ser.beginWriteDictionaryEntry!STraits(underscoreStrip(TU.fieldNames[i])); 392 ser.serializeValue!(TV, ATTRIBUTES)(value[i]); 393 ser.endWriteDictionaryEntry!STraits(underscoreStrip(TU.fieldNames[i])); 394 } 395 static if (__traits(compiles, ser.endWriteDictionary!TU(0))) { 396 ser.endWriteDictionary!Traits(nfields); 397 } else { 398 ser.endWriteDictionary!Traits(); 399 } 400 } else static if (TU.Types.length == 1) { 401 ser.serializeValue!(typeof(value[0]), ATTRIBUTES)(value[0]); 402 } else { 403 ser.beginWriteArray!Traits(value.length); 404 foreach (i, TV; T.Types) { 405 alias STraits = SubTraits!(Traits, TV); 406 ser.beginWriteArrayEntry!STraits(i); 407 ser.serializeValue!(TV, ATTRIBUTES)(value[i]); 408 ser.endWriteArrayEntry!STraits(i); 409 } 410 ser.endWriteArray!Traits(); 411 } 412 } else static if (isArray!TU) { 413 alias TV = typeof(value[0]); 414 alias STraits = SubTraits!(Traits, TV); 415 ser.beginWriteArray!Traits(value.length); 416 foreach (i, ref el; value) { 417 ser.beginWriteArrayEntry!STraits(i); 418 ser.serializeValue!(TV, ATTRIBUTES)(el); 419 ser.endWriteArrayEntry!STraits(i); 420 } 421 ser.endWriteArray!Traits(); 422 } else static if (isAssociativeArray!TU) { 423 alias TK = KeyType!TU; 424 alias TV = ValueType!TU; 425 alias STraits = SubTraits!(Traits, TV); 426 427 static if (__traits(compiles, ser.beginWriteDictionary!TU(0))) { 428 auto nfields = value.length; 429 ser.beginWriteDictionary!Traits(nfields); 430 } else { 431 ser.beginWriteDictionary!Traits(); 432 } 433 foreach (key, ref el; value) { 434 string keyname; 435 static if (is(TK : string)) keyname = key; 436 else static if (is(TK : real) || is(TK : long) || is(TK == enum)) keyname = key.to!string; 437 else static if (isStringSerializable!TK) keyname = key.toString(); 438 else static assert(false, "Associative array keys must be strings, numbers, enums, or have toString/fromString methods."); 439 ser.beginWriteDictionaryEntry!STraits(keyname); 440 ser.serializeValue!(TV, ATTRIBUTES)(el); 441 ser.endWriteDictionaryEntry!STraits(keyname); 442 } 443 static if (__traits(compiles, ser.endWriteDictionary!TU(0))) { 444 ser.endWriteDictionary!Traits(nfields); 445 } else { 446 ser.endWriteDictionary!Traits(); 447 } 448 } else static if (/*isInstanceOf!(Nullable, TU)*/is(T == Nullable!TPS, TPS...)) { 449 if (value.isNull()) ser.serializeValue!(typeof(null))(null); 450 else ser.serializeValue!(typeof(value.get()), ATTRIBUTES)(value.get()); 451 } else static if (isInstanceOf!(Typedef, TU)) { 452 ser.serializeValue!(TypedefType!TU, ATTRIBUTES)(cast(TypedefType!TU)value); 453 } else static if (is(TU == BitFlags!E, E)) { 454 alias STraits = SubTraits!(Traits, E); 455 456 size_t cnt = 0; 457 foreach (v; EnumMembers!E) 458 if (value & v) 459 cnt++; 460 461 ser.beginWriteArray!Traits(cnt); 462 cnt = 0; 463 foreach (v; EnumMembers!E) 464 if (value & v) { 465 ser.beginWriteArrayEntry!STraits(cnt); 466 ser.serializeValue!(E, ATTRIBUTES)(v); 467 ser.endWriteArrayEntry!STraits(cnt); 468 cnt++; 469 } 470 ser.endWriteArray!Traits(); 471 } else static if (isCustomSerializable!TU) { 472 alias CustomType = typeof(T.init.toRepresentation()); 473 ser.serializeValue!(CustomType, ATTRIBUTES)(value.toRepresentation()); 474 } else static if (isISOExtStringSerializable!TU) { 475 ser.serializeValue!(string, ATTRIBUTES)(value.toISOExtString()); 476 } else static if (isStringSerializable!TU) { 477 ser.serializeValue!(string, ATTRIBUTES)(value.toString()); 478 } else static if (is(TU == struct) || is(TU == class)) { 479 static if (!hasSerializableFields!(TU, Policy)) 480 pragma(msg, "Serializing composite type "~T.stringof~" which has no serializable fields"); 481 static if (is(TU == class)) { 482 if (value is null) { 483 ser.serializeValue!(typeof(null))(null); 484 return; 485 } 486 } 487 static if (hasPolicyAttributeL!(AsArrayAttribute, Policy, ATTRIBUTES)) { 488 enum nfields = getExpandedFieldCount!(TU, SerializableFields!(TU, Policy)); 489 ser.beginWriteArray!Traits(nfields); 490 size_t fcount = 0; 491 foreach (mname; SerializableFields!(TU, Policy)) { 492 alias TMS = TypeTuple!(typeof(__traits(getMember, value, mname))); 493 foreach (j, TM; TMS) { 494 alias TA = TypeTuple!(__traits(getAttributes, TypeTuple!(__traits(getMember, T, mname))[j])); 495 alias STraits = SubTraits!(Traits, TM, TA); 496 ser.beginWriteArrayEntry!STraits(fcount); 497 ser.serializeValue!(TM, TA)(tuple(__traits(getMember, value, mname))[j]); 498 ser.endWriteArrayEntry!STraits(fcount); 499 fcount++; 500 } 501 } 502 ser.endWriteArray!Traits(); 503 } else { 504 static if (__traits(compiles, ser.beginWriteDictionary!Traits(0))) { 505 enum nfields = getExpandedFieldCount!(TU, SerializableFields!(TU, Policy)); 506 ser.beginWriteDictionary!Traits(nfields); 507 } else { 508 ser.beginWriteDictionary!Traits(); 509 } 510 foreach (mname; SerializableFields!(TU, Policy)) { 511 alias TM = TypeTuple!(typeof(__traits(getMember, TU, mname))); 512 alias TA = TypeTuple!(__traits(getAttributes, TypeTuple!(__traits(getMember, T, mname))[0])); 513 enum name = getPolicyAttribute!(TU, mname, NameAttribute, Policy)(NameAttribute!DefaultPolicy(underscoreStrip(mname))).name; 514 static if (TM.length == 1) { 515 auto vt = __traits(getMember, value, mname); 516 } else { 517 auto vt = tuple!TM(__traits(getMember, value, mname)); 518 } 519 alias STraits = SubTraits!(Traits, typeof(vt), TA); 520 ser.beginWriteDictionaryEntry!STraits(name); 521 ser.serializeValue!(typeof(vt), TA)(vt); 522 ser.endWriteDictionaryEntry!STraits(name); 523 } 524 static if (__traits(compiles, ser.endWriteDictionary!Traits(0))) { 525 ser.endWriteDictionary!Traits(nfields); 526 } else { 527 ser.endWriteDictionary!Traits(); 528 } 529 } 530 } else static if (isPointer!TU) { 531 if (value is null) { 532 ser.writeValue!Traits(null); 533 return; 534 } 535 ser.serializeValue!(PointerTarget!TU)(*value); 536 } else static if (is(TU == bool) || is(TU : real) || is(TU : long)) { 537 ser.serializeValue!(string, ATTRIBUTES)(to!string(value)); 538 } else static assert(false, "Unsupported serialization type: " ~ T.stringof); 539 } 540 } 541 542 private struct Traits(T, alias POL, ATTRIBUTES...) 543 { 544 alias Type = T; 545 alias Policy = POL; 546 alias Attributes = TypeTuple!ATTRIBUTES; 547 } 548 549 private struct SubTraits(Traits, T, A...) 550 { 551 alias Type = Unqual!T; 552 alias Attributes = TypeTuple!A; 553 alias Policy = Traits.Policy; 554 alias ContainerType = Traits.Type; 555 alias ContainerAttributes = Traits.Attributes; 556 } 557 558 private template deserializeValueImpl(Serializer, alias Policy) { 559 alias _Policy = Policy; 560 static assert(Serializer.isSupportedValueType!string, "All serializers must support string values."); 561 static assert(Serializer.isSupportedValueType!(typeof(null)), "All serializers must support null values."); 562 563 // work around https://issues.dlang.org/show_bug.cgi?id=16528 564 static if (isSafeSerializer!Serializer) { 565 T deserializeValue(T, ATTRIBUTES...)(ref Serializer ser) @safe { return deserializeValueDeduced!(T, ATTRIBUTES)(ser); } 566 } else { 567 T deserializeValue(T, ATTRIBUTES...)(ref Serializer ser) { return deserializeValueDeduced!(T, ATTRIBUTES)(ser); } 568 } 569 570 T deserializeValueDeduced(T, ATTRIBUTES...)(ref Serializer ser) if(!isMutable!T) 571 { 572 import std.algorithm.mutation : move; 573 auto ret = deserializeValue!(Unqual!T, ATTRIBUTES)(ser); 574 return () @trusted { return cast(T)ret.move; } (); 575 } 576 577 T deserializeValueDeduced(T, ATTRIBUTES...)(ref Serializer ser) if(isMutable!T) 578 { 579 import std.typecons : BitFlags, Nullable, Typedef, TypedefType, Tuple; 580 581 alias Traits = .Traits!(T, _Policy, ATTRIBUTES); 582 583 static if (isPolicySerializable!(Policy, T)) { 584 alias CustomType = typeof(Policy!T.toRepresentation(T.init)); 585 return Policy!T.fromRepresentation(ser.deserializeValue!(CustomType, ATTRIBUTES)); 586 } else static if (is(T == enum)) { 587 static if (hasPolicyAttributeL!(ByNameAttribute, Policy, ATTRIBUTES)) { 588 return ser.deserializeValue!(string, ATTRIBUTES).to!T(); 589 } else { 590 return cast(T)ser.deserializeValue!(OriginalType!T); 591 } 592 } else static if (Serializer.isSupportedValueType!T) { 593 return ser.readValue!(Traits, T)(); 594 } else static if (/*isInstanceOf!(Tuple, TU)*/is(T == Tuple!TPS, TPS...)) { 595 enum fieldsCount = T.Types.length; 596 import std.algorithm.searching: all; 597 static if (all!"!a.empty"([T.fieldNames]) && 598 !hasPolicyAttributeL!(AsArrayAttribute, Policy, ATTRIBUTES)) { 599 T ret; 600 bool[fieldsCount] set; 601 ser.readDictionary!Traits((name) { 602 switch (name) { 603 default: break; 604 foreach (i, TV; T.Types) { 605 enum fieldName = underscoreStrip(T.fieldNames[i]); 606 alias STraits = SubTraits!(Traits, TV); 607 case fieldName: { 608 ser.beginReadDictionaryEntry!STraits(fieldName); 609 ret[i] = ser.deserializeValue!(TV, ATTRIBUTES); 610 ser.endReadDictionaryEntry!STraits(fieldName); 611 set[i] = true; 612 } break; 613 } 614 } 615 }); 616 foreach (i, fieldName; T.fieldNames) 617 enforce(set[i], "Missing tuple field '"~fieldName~"' of type '"~T.Types[i].stringof~"' ("~Policy.stringof~")."); 618 return ret; 619 } else static if (fieldsCount == 1) { 620 return T(ser.deserializeValue!(T.Types[0], ATTRIBUTES)()); 621 } else { 622 T ret; 623 size_t currentField = 0; 624 ser.readArray!Traits((sz) { assert(sz == 0 || sz == fieldsCount); }, { 625 switch (currentField++) { 626 default: break; 627 foreach (i, TV; T.Types) { 628 alias STraits = SubTraits!(Traits, TV); 629 case i: { 630 ser.beginReadArrayEntry!STraits(i); 631 ret[i] = ser.deserializeValue!(TV, ATTRIBUTES); 632 ser.endReadArrayEntry!STraits(i); 633 } break; 634 } 635 } 636 }); 637 enforce(currentField == fieldsCount, "Missing tuple field(s) - expected '"~fieldsCount.stringof~"', received '"~currentField.stringof~"' ("~Policy.stringof~")."); 638 return ret; 639 } 640 } else static if (isStaticArray!T) { 641 alias TV = typeof(T.init[0]); 642 alias STraits = SubTraits!(Traits, TV); 643 T ret; 644 size_t i = 0; 645 ser.readArray!Traits((sz) { assert(sz == 0 || sz == T.length); }, { 646 assert(i < T.length); 647 ser.beginReadArrayEntry!STraits(i); 648 ret[i] = ser.deserializeValue!(TV, ATTRIBUTES); 649 ser.endReadArrayEntry!STraits(i); 650 i++; 651 }); 652 return ret; 653 } else static if (isDynamicArray!T) { 654 alias TV = typeof(T.init[0]); 655 alias STraits = SubTraits!(Traits, TV); 656 //auto ret = appender!T(); 657 T ret; // Cannot use appender because of DMD BUG 10690/10859/11357 658 ser.readArray!Traits((sz) @safe { ret.reserve(sz); }, () @safe { 659 size_t i = ret.length; 660 ser.beginReadArrayEntry!STraits(i); 661 static if (__traits(compiles, () @safe { ser.deserializeValue!(TV, ATTRIBUTES); })) 662 ret ~= ser.deserializeValue!(TV, ATTRIBUTES); 663 else // recursive array https://issues.dlang.org/show_bug.cgi?id=16528 664 ret ~= (() @trusted => ser.deserializeValue!(TV, ATTRIBUTES))(); 665 ser.endReadArrayEntry!STraits(i); 666 }); 667 return ret;//cast(T)ret.data; 668 } else static if (isAssociativeArray!T) { 669 alias TK = KeyType!T; 670 alias TV = ValueType!T; 671 alias STraits = SubTraits!(Traits, TV); 672 673 T ret; 674 ser.readDictionary!Traits((name) @safe { 675 TK key; 676 static if (is(TK == string) || (is(TK == enum) && is(OriginalType!TK == string))) key = cast(TK)name; 677 else static if (is(TK : real) || is(TK : long) || is(TK == enum)) key = name.to!TK; 678 else static if (isStringSerializable!TK) key = TK.fromString(name); 679 else static assert(false, "Associative array keys must be strings, numbers, enums, or have toString/fromString methods."); 680 ser.beginReadDictionaryEntry!STraits(name); 681 ret[key] = ser.deserializeValue!(TV, ATTRIBUTES); 682 ser.endReadDictionaryEntry!STraits(name); 683 }); 684 return ret; 685 } else static if (isInstanceOf!(Nullable, T)) { 686 if (ser.tryReadNull!Traits()) return T.init; 687 return T(ser.deserializeValue!(typeof(T.init.get()), ATTRIBUTES)); 688 } else static if (isInstanceOf!(Typedef, T)) { 689 return T(ser.deserializeValue!(TypedefType!T, ATTRIBUTES)); 690 } else static if (is(T == BitFlags!E, E)) { 691 alias STraits = SubTraits!(Traits, E); 692 T ret; 693 size_t i = 0; 694 ser.readArray!Traits((sz) {}, { 695 ser.beginReadArrayEntry!STraits(i); 696 ret |= ser.deserializeValue!(E, ATTRIBUTES); 697 ser.endReadArrayEntry!STraits(i); 698 i++; 699 }); 700 return ret; 701 } else static if (isCustomSerializable!T) { 702 alias CustomType = typeof(T.init.toRepresentation()); 703 return T.fromRepresentation(ser.deserializeValue!(CustomType, ATTRIBUTES)); 704 } else static if (isISOExtStringSerializable!T) { 705 return T.fromISOExtString(ser.readValue!(Traits, string)()); 706 } else static if (isStringSerializable!T) { 707 return T.fromString(ser.readValue!(Traits, string)()); 708 } else static if (is(T == struct) || is(T == class)) { 709 static if (is(T == class)) { 710 if (ser.tryReadNull!Traits()) return null; 711 } 712 713 T ret; 714 string name; 715 bool[getExpandedFieldsData!(T, SerializableFields!(T, Policy)).length] set; 716 static if (is(T == class)) ret = new T; 717 718 static if (hasPolicyAttributeL!(AsArrayAttribute, Policy, ATTRIBUTES)) { 719 size_t idx = 0; 720 ser.readArray!Traits((sz){}, { 721 static if (hasSerializableFields!(T, Policy)) { 722 switch (idx++) { 723 default: break; 724 foreach (i, FD; getExpandedFieldsData!(T, SerializableFields!(T, Policy))) { 725 enum mname = FD[0]; 726 enum msindex = FD[1]; 727 alias MT = TypeTuple!(__traits(getMember, T, mname)); 728 alias MTI = MT[msindex]; 729 alias TMTI = typeof(MTI); 730 alias TMTIA = TypeTuple!(__traits(getAttributes, MTI)); 731 alias STraits = SubTraits!(Traits, TMTI, TMTIA); 732 733 case i: 734 static if (hasPolicyAttribute!(OptionalAttribute, Policy, MTI)) 735 if (ser.tryReadNull!STraits()) return; 736 set[i] = true; 737 ser.beginReadArrayEntry!STraits(i); 738 static if (MT.length == 1) { 739 __traits(getMember, ret, mname) = ser.deserializeValue!(TMTI, TMTIA); 740 } else { 741 __traits(getMember, ret, mname)[msindex] = ser.deserializeValue!(TMTI, TMTIA); 742 } 743 ser.endReadArrayEntry!STraits(i); 744 break; 745 } 746 } 747 } else { 748 pragma(msg, "Deserializing composite type "~T.stringof~" which has no serializable fields."); 749 } 750 }); 751 } else { 752 ser.readDictionary!Traits((name) { 753 static if (hasSerializableFields!(T, Policy)) { 754 switch (name) { 755 default: break; 756 foreach (i, mname; SerializableFields!(T, Policy)) { 757 alias TM = TypeTuple!(typeof(__traits(getMember, T, mname))); 758 alias TA = TypeTuple!(__traits(getAttributes, TypeTuple!(__traits(getMember, T, mname))[0])); 759 alias STraits = SubTraits!(Traits, TM, TA); 760 enum fname = getPolicyAttribute!(T, mname, NameAttribute, Policy)(NameAttribute!DefaultPolicy(underscoreStrip(mname))).name; 761 case fname: 762 static if (hasPolicyAttribute!(OptionalAttribute, Policy, TypeTuple!(__traits(getMember, T, mname))[0])) 763 if (ser.tryReadNull!STraits()) return; 764 set[i] = true; 765 ser.beginReadDictionaryEntry!STraits(fname); 766 static if (TM.length == 1) { 767 __traits(getMember, ret, mname) = ser.deserializeValue!(TM, TA); 768 } else { 769 __traits(getMember, ret, mname) = ser.deserializeValue!(Tuple!TM, TA); 770 } 771 ser.endReadDictionaryEntry!STraits(fname); 772 break; 773 } 774 } 775 } else { 776 pragma(msg, "Deserializing composite type "~T.stringof~" which has no serializable fields."); 777 } 778 }); 779 } 780 foreach (i, mname; SerializableFields!(T, Policy)) 781 static if (!hasPolicyAttribute!(OptionalAttribute, Policy, TypeTuple!(__traits(getMember, T, mname))[0])) 782 enforce(set[i], "Missing non-optional field '"~mname~"' of type '"~T.stringof~"' ("~Policy.stringof~")."); 783 return ret; 784 } else static if (isPointer!T) { 785 if (ser.tryReadNull!Traits()) return null; 786 alias PT = PointerTarget!T; 787 auto ret = new PT; 788 *ret = ser.deserializeValue!(PT, ATTRIBUTES); 789 return ret; 790 } else static if (is(T == bool) || is(T : real) || is(T : long)) { 791 return to!T(ser.deserializeValue!string()); 792 } else static assert(false, "Unsupported serialization type: " ~ T.stringof); 793 } 794 } 795 796 797 /** 798 Attribute for overriding the field name during (de-)serialization. 799 800 Note that without the `@name` attribute there is a shorter alternative 801 for using names that collide with a D keyword. A single trailing 802 underscore will automatically be stripped when determining a field 803 name. 804 */ 805 NameAttribute!Policy name(alias Policy = DefaultPolicy)(string name) 806 { 807 return NameAttribute!Policy(name); 808 } 809 /// 810 unittest { 811 struct CustomPolicy {} 812 813 struct Test { 814 // serialized as "screen-size": 815 @name("screen-size") int screenSize; 816 817 // serialized as "print-size" by default, 818 // but as "PRINTSIZE" if CustomPolicy is used for serialization. 819 @name("print-size") 820 @name!CustomPolicy("PRINTSIZE") 821 int printSize; 822 823 // serialized as "version" 824 int version_; 825 } 826 } 827 828 829 /** 830 Attribute marking a field as optional during deserialization. 831 */ 832 @property OptionalAttribute!Policy optional(alias Policy = DefaultPolicy)() 833 { 834 return OptionalAttribute!Policy(); 835 } 836 /// 837 unittest { 838 struct Test { 839 // does not need to be present during deserialization 840 @optional int screenSize = 100; 841 } 842 } 843 844 845 /** 846 Attribute for marking non-serialized fields. 847 */ 848 @property IgnoreAttribute!Policy ignore(alias Policy = DefaultPolicy)() 849 { 850 return IgnoreAttribute!Policy(); 851 } 852 /// 853 unittest { 854 struct Test { 855 // is neither serialized not deserialized 856 @ignore int screenSize; 857 } 858 } 859 /// 860 unittest { 861 template CustomPolicy(T) { 862 // ... 863 } 864 865 struct Test { 866 // not (de)serialized for serializeWithPolicy!(Test, CustomPolicy) 867 // but for other policies or when serialized without a policy 868 @ignore!CustomPolicy int screenSize; 869 } 870 } 871 872 873 /** 874 Attribute for forcing serialization of enum fields by name instead of by value. 875 */ 876 @property ByNameAttribute!Policy byName(alias Policy = DefaultPolicy)() 877 { 878 return ByNameAttribute!Policy(); 879 } 880 /// 881 unittest { 882 enum Color { 883 red, 884 green, 885 blue 886 } 887 888 struct Test { 889 // serialized as an int (e.g. 1 for Color.green) 890 Color color; 891 // serialized as a string (e.g. "green" for Color.green) 892 @byName Color namedColor; 893 // serialized as array of ints 894 Color[] colorArray; 895 // serialized as array of strings 896 @byName Color[] namedColorArray; 897 } 898 } 899 900 901 /** 902 Attribute for representing a struct/class as an array instead of an object. 903 904 Usually structs and class objects are serialized as dictionaries mapping 905 from field name to value. Using this attribute, they will be serialized 906 as a flat array instead. Note that changing the layout will make any 907 already serialized data mismatch when this attribute is used. 908 */ 909 @property AsArrayAttribute!Policy asArray(alias Policy = DefaultPolicy)() 910 { 911 return AsArrayAttribute!Policy(); 912 } 913 /// 914 unittest { 915 struct Fields { 916 int f1; 917 string f2; 918 double f3; 919 } 920 921 struct Test { 922 // serialized as name:value pairs ["f1": int, "f2": string, "f3": double] 923 Fields object; 924 // serialized as a sequential list of values [int, string, double] 925 @asArray Fields array; 926 } 927 928 import vibe.data.json; 929 static assert(is(typeof(serializeToJson(Test())))); 930 } 931 932 933 /// 934 enum FieldExistence 935 { 936 missing, 937 exists, 938 defer 939 } 940 941 /// User defined attribute (not intended for direct use) 942 struct NameAttribute(alias POLICY) { alias Policy = POLICY; string name; } 943 /// ditto 944 struct OptionalAttribute(alias POLICY) { alias Policy = POLICY; } 945 /// ditto 946 struct IgnoreAttribute(alias POLICY) { alias Policy = POLICY; } 947 /// ditto 948 struct ByNameAttribute(alias POLICY) { alias Policy = POLICY; } 949 /// ditto 950 struct AsArrayAttribute(alias POLICY) { alias Policy = POLICY; } 951 952 /** 953 Checks if a given type has a custom serialization representation. 954 955 A class or struct type is custom serializable if it defines a pair of 956 `toRepresentation`/`fromRepresentation` methods. Any class or 957 struct type that has this trait will be serialized by using the return 958 value of it's `toRepresentation` method instead of the original value. 959 960 This trait has precedence over `isISOExtStringSerializable` and 961 `isStringSerializable`. 962 */ 963 template isCustomSerializable(T) 964 { 965 enum bool isCustomSerializable = is(typeof(T.init.toRepresentation())) && is(typeof(T.fromRepresentation(T.init.toRepresentation())) == T); 966 } 967 /// 968 unittest { 969 // represented as a single uint when serialized 970 static struct S { 971 ushort x, y; 972 973 uint toRepresentation() const { return x + (y << 16); } 974 static S fromRepresentation(uint i) { return S(i & 0xFFFF, i >> 16); } 975 } 976 977 static assert(isCustomSerializable!S); 978 } 979 980 981 /** 982 Checks if a given type has an ISO extended string serialization representation. 983 984 A class or struct type is ISO extended string serializable if it defines a 985 pair of `toISOExtString`/`fromISOExtString` methods. Any class or 986 struct type that has this trait will be serialized by using the return 987 value of it's `toISOExtString` method instead of the original value. 988 989 This is mainly useful for supporting serialization of the the date/time 990 types in `std.datetime`. 991 992 This trait has precedence over `isStringSerializable`. 993 */ 994 template isISOExtStringSerializable(T) 995 { 996 enum bool isISOExtStringSerializable = is(typeof(T.init.toISOExtString()) == string) && is(typeof(T.fromISOExtString("")) == T); 997 } 998 /// 999 unittest { 1000 import std.datetime; 1001 1002 static assert(isISOExtStringSerializable!DateTime); 1003 static assert(isISOExtStringSerializable!SysTime); 1004 1005 // represented as an ISO extended string when serialized 1006 static struct S { 1007 // dummy example implementations 1008 string toISOExtString() const { return ""; } 1009 static S fromISOExtString(string s) { return S.init; } 1010 } 1011 1012 static assert(isISOExtStringSerializable!S); 1013 } 1014 1015 1016 /** 1017 Checks if a given type has a string serialization representation. 1018 1019 A class or struct type is string serializable if it defines a pair of 1020 `toString`/`fromString` methods. Any class or struct type that 1021 has this trait will be serialized by using the return value of it's 1022 `toString` method instead of the original value. 1023 */ 1024 template isStringSerializable(T) 1025 { 1026 enum bool isStringSerializable = is(typeof(T.init.toString()) == string) && is(typeof(T.fromString("")) == T); 1027 } 1028 /// 1029 unittest { 1030 import std.conv; 1031 1032 // represented as a string when serialized 1033 static struct S { 1034 int value; 1035 1036 // dummy example implementations 1037 string toString() const { return value.to!string(); } 1038 static S fromString(string s) { return S(s.to!int()); } 1039 } 1040 1041 static assert(isStringSerializable!S); 1042 } 1043 1044 1045 /** Default policy (performs no customization). 1046 */ 1047 template DefaultPolicy(T) 1048 { 1049 } 1050 1051 /** 1052 Checks if a given policy supports custom serialization for a given type. 1053 1054 A class or struct type is custom serializable according to a policy if 1055 the policy defines a pair of `toRepresentation`/`fromRepresentation` 1056 functions. Any class or struct type that has this trait for the policy supplied to 1057 `serializeWithPolicy` will be serialized by using the return value of the 1058 policy `toRepresentation` function instead of the original value. 1059 1060 This trait has precedence over `isCustomSerializable`, 1061 `isISOExtStringSerializable` and `isStringSerializable`. 1062 1063 See_Also: `vibe.data.serialization.serializeWithPolicy` 1064 */ 1065 template isPolicySerializable(alias Policy, T) 1066 { 1067 enum bool isPolicySerializable = is(typeof(Policy!T.toRepresentation(T.init))) && 1068 is(typeof(Policy!T.fromRepresentation(Policy!T.toRepresentation(T.init))) == T); 1069 } 1070 /// 1071 unittest { 1072 import std.conv; 1073 1074 // represented as the boxed value when serialized 1075 static struct Box(T) { 1076 T value; 1077 } 1078 1079 template BoxPol(S) 1080 { 1081 auto toRepresentation(S s) { 1082 return s.value; 1083 } 1084 1085 S fromRepresentation(typeof(S.init.value) v) { 1086 return S(v); 1087 } 1088 } 1089 static assert(isPolicySerializable!(BoxPol, Box!int)); 1090 } 1091 1092 1093 /** 1094 Chains serialization policy. 1095 1096 Constructs a serialization policy that given a type `T` will apply the 1097 first compatible policy `toRepresentation` and `fromRepresentation` 1098 functions. Policies are evaluated left-to-right according to 1099 `isPolicySerializable`. 1100 1101 See_Also: `vibe.data.serialization.serializeWithPolicy` 1102 */ 1103 template ChainedPolicy(alias Primary, Fallbacks...) 1104 { 1105 static if (Fallbacks.length == 0) { 1106 alias ChainedPolicy = Primary; 1107 } else { 1108 alias ChainedPolicy = ChainedPolicy!(ChainedPolicyImpl!(Primary, Fallbacks[0]), Fallbacks[1..$]); 1109 } 1110 } 1111 /// 1112 unittest { 1113 import std.conv; 1114 1115 // To be represented as the boxed value when serialized 1116 static struct Box(T) { 1117 T value; 1118 } 1119 // Also to berepresented as the boxed value when serialized, but has 1120 // a different way to access the value. 1121 static struct Box2(T) { 1122 private T v; 1123 ref T get() { 1124 return v; 1125 } 1126 } 1127 template BoxPol(S) 1128 { 1129 auto toRepresentation(S s) { 1130 return s.value; 1131 } 1132 1133 S fromRepresentation(typeof(toRepresentation(S.init)) v) { 1134 return S(v); 1135 } 1136 } 1137 template Box2Pol(S) 1138 { 1139 auto toRepresentation(S s) { 1140 return s.get(); 1141 } 1142 1143 S fromRepresentation(typeof(toRepresentation(S.init)) v) { 1144 S s; 1145 s.get() = v; 1146 return s; 1147 } 1148 } 1149 alias ChainPol = ChainedPolicy!(BoxPol, Box2Pol); 1150 static assert(!isPolicySerializable!(BoxPol, Box2!int)); 1151 static assert(!isPolicySerializable!(Box2Pol, Box!int)); 1152 static assert(isPolicySerializable!(ChainPol, Box!int)); 1153 static assert(isPolicySerializable!(ChainPol, Box2!int)); 1154 } 1155 1156 private template ChainedPolicyImpl(alias Primary, alias Fallback) 1157 { 1158 template Pol(T) 1159 { 1160 static if (isPolicySerializable!(Primary, T)) { 1161 alias toRepresentation = Primary!T.toRepresentation; 1162 alias fromRepresentation = Primary!T.fromRepresentation; 1163 } else { 1164 alias toRepresentation = Fallback!T.toRepresentation; 1165 alias fromRepresentation = Fallback!T.fromRepresentation; 1166 } 1167 } 1168 alias ChainedPolicyImpl = Pol; 1169 } 1170 1171 // heuristically determines @safe'ty of the serializer by testing readValue and writeValue for type int 1172 private enum isSafeSerializer(S) = __traits(compiles, (S s) @safe { 1173 alias T = Traits!(int, DefaultPolicy); 1174 s.writeValue!T(42); 1175 s.readValue!(T, int)(); 1176 }); 1177 1178 private template hasAttribute(T, alias decl) { enum hasAttribute = findFirstUDA!(T, decl).found; } 1179 1180 unittest { 1181 @asArray int i1; 1182 static assert(hasAttribute!(AsArrayAttribute!DefaultPolicy, i1)); 1183 int i2; 1184 static assert(!hasAttribute!(AsArrayAttribute!DefaultPolicy, i2)); 1185 } 1186 1187 private template hasPolicyAttribute(alias T, alias POLICY, alias decl) 1188 { 1189 enum hasPolicyAttribute = hasAttribute!(T!POLICY, decl) || hasAttribute!(T!DefaultPolicy, decl); 1190 } 1191 1192 unittest { 1193 template CP(T) {} 1194 @asArray!CP int i1; 1195 @asArray int i2; 1196 int i3; 1197 static assert(hasPolicyAttribute!(AsArrayAttribute, CP, i1)); 1198 static assert(hasPolicyAttribute!(AsArrayAttribute, CP, i2)); 1199 static assert(!hasPolicyAttribute!(AsArrayAttribute, CP, i3)); 1200 static assert(!hasPolicyAttribute!(AsArrayAttribute, DefaultPolicy, i1)); 1201 static assert(hasPolicyAttribute!(AsArrayAttribute, DefaultPolicy, i2)); 1202 static assert(!hasPolicyAttribute!(AsArrayAttribute, DefaultPolicy, i3)); 1203 } 1204 1205 1206 private template hasAttributeL(T, ATTRIBUTES...) { 1207 static if (ATTRIBUTES.length == 1) { 1208 enum hasAttributeL = is(typeof(ATTRIBUTES[0]) == T); 1209 } else static if (ATTRIBUTES.length > 1) { 1210 enum hasAttributeL = hasAttributeL!(T, ATTRIBUTES[0 .. $/2]) || hasAttributeL!(T, ATTRIBUTES[$/2 .. $]); 1211 } else { 1212 enum hasAttributeL = false; 1213 } 1214 } 1215 1216 unittest { 1217 static assert(hasAttributeL!(AsArrayAttribute!DefaultPolicy, byName, asArray)); 1218 static assert(!hasAttributeL!(AsArrayAttribute!DefaultPolicy, byName)); 1219 } 1220 1221 private template hasPolicyAttributeL(alias T, alias POLICY, ATTRIBUTES...) 1222 { 1223 enum hasPolicyAttributeL = hasAttributeL!(T!POLICY, ATTRIBUTES) || hasAttributeL!(T!DefaultPolicy, ATTRIBUTES); 1224 } 1225 1226 private static T getAttribute(TT, string mname, T)(T default_value) 1227 { 1228 enum val = findFirstUDA!(T, __traits(getMember, TT, mname)); 1229 static if (val.found) return val.value; 1230 else return default_value; 1231 } 1232 1233 private static auto getPolicyAttribute(TT, string mname, alias Attribute, alias Policy)(Attribute!DefaultPolicy default_value) 1234 { 1235 enum val = findFirstUDA!(Attribute!Policy, TypeTuple!(__traits(getMember, TT, mname))[0]); 1236 static if (val.found) return val.value; 1237 else { 1238 enum val2 = findFirstUDA!(Attribute!DefaultPolicy, TypeTuple!(__traits(getMember, TT, mname))[0]); 1239 static if (val2.found) return val2.value; 1240 else return default_value; 1241 } 1242 } 1243 1244 private string underscoreStrip(string field_name) 1245 @safe nothrow @nogc { 1246 if( field_name.length < 1 || field_name[$-1] != '_' ) return field_name; 1247 else return field_name[0 .. $-1]; 1248 } 1249 1250 1251 private template hasSerializableFields(T, alias POLICY, size_t idx = 0) 1252 { 1253 enum hasSerializableFields = SerializableFields!(T, POLICY).length > 0; 1254 /*static if (idx < __traits(allMembers, T).length) { 1255 enum mname = __traits(allMembers, T)[idx]; 1256 static if (!isRWPlainField!(T, mname) && !isRWField!(T, mname)) enum hasSerializableFields = hasSerializableFields!(T, idx+1); 1257 else static if (hasAttribute!(IgnoreAttribute, __traits(getMember, T, mname))) enum hasSerializableFields = hasSerializableFields!(T, idx+1); 1258 else enum hasSerializableFields = true; 1259 } else enum hasSerializableFields = false;*/ 1260 } 1261 1262 private template SerializableFields(COMPOSITE, alias POLICY) 1263 { 1264 alias SerializableFields = FilterSerializableFields!(COMPOSITE, POLICY, __traits(allMembers, COMPOSITE)); 1265 } 1266 1267 private template FilterSerializableFields(COMPOSITE, alias POLICY, FIELDS...) 1268 { 1269 static if (FIELDS.length > 1) { 1270 alias FilterSerializableFields = TypeTuple!( 1271 FilterSerializableFields!(COMPOSITE, POLICY, FIELDS[0 .. $/2]), 1272 FilterSerializableFields!(COMPOSITE, POLICY, FIELDS[$/2 .. $])); 1273 } else static if (FIELDS.length == 1) { 1274 alias T = COMPOSITE; 1275 enum mname = FIELDS[0]; 1276 static if (isRWPlainField!(T, mname) || isRWField!(T, mname)) { 1277 alias Tup = TypeTuple!(__traits(getMember, COMPOSITE, FIELDS[0])); 1278 static if (Tup.length != 1) { 1279 alias FilterSerializableFields = TypeTuple!(mname); 1280 } else { 1281 static if (!hasPolicyAttribute!(IgnoreAttribute, POLICY, __traits(getMember, T, mname))) 1282 { 1283 alias FilterSerializableFields = TypeTuple!(mname); 1284 } else alias FilterSerializableFields = TypeTuple!(); 1285 } 1286 } else alias FilterSerializableFields = TypeTuple!(); 1287 } else alias FilterSerializableFields = TypeTuple!(); 1288 } 1289 1290 private size_t getExpandedFieldCount(T, FIELDS...)() 1291 { 1292 size_t ret = 0; 1293 foreach (F; FIELDS) ret += TypeTuple!(__traits(getMember, T, F)).length; 1294 return ret; 1295 } 1296 1297 private template getExpandedFieldsData(T, FIELDS...) 1298 { 1299 import std.meta : aliasSeqOf, staticMap; 1300 import std.range : repeat, zip, iota; 1301 1302 enum subfieldsCount(alias F) = TypeTuple!(__traits(getMember, T, F)).length; 1303 alias processSubfield(alias F) = aliasSeqOf!(zip(repeat(F), iota(subfieldsCount!F))); 1304 alias getExpandedFieldsData = staticMap!(processSubfield, FIELDS); 1305 } 1306 1307 /******************************************************************************/ 1308 /* General serialization unit testing */ 1309 /******************************************************************************/ 1310 1311 version (unittest) { 1312 static assert(isSafeSerializer!TestSerializer); 1313 1314 private struct TestSerializer { 1315 import std.array, std.conv, std.string; 1316 1317 string result; 1318 1319 enum isSupportedValueType(T) = is(T == string) || is(T == typeof(null)) || is(T == float) || is (T == int); 1320 1321 string getSerializedResult() @safe { return result; } 1322 void beginWriteDictionary(Traits)() { result ~= "D("~Traits.Type.mangleof~"){"; } 1323 void endWriteDictionary(Traits)() { result ~= "}D("~Traits.Type.mangleof~")"; } 1324 void beginWriteDictionaryEntry(Traits)(string name) { result ~= "DE("~Traits.Type.mangleof~","~name~")("; } 1325 void endWriteDictionaryEntry(Traits)(string name) { result ~= ")DE("~Traits.Type.mangleof~","~name~")"; } 1326 void beginWriteArray(Traits)(size_t length) { result ~= "A("~Traits.Type.mangleof~")["~length.to!string~"]["; } 1327 void endWriteArray(Traits)() { result ~= "]A("~Traits.Type.mangleof~")"; } 1328 void beginWriteArrayEntry(Traits)(size_t i) { result ~= "AE("~Traits.Type.mangleof~","~i.to!string~")("; } 1329 void endWriteArrayEntry(Traits)(size_t i) { result ~= ")AE("~Traits.Type.mangleof~","~i.to!string~")"; } 1330 void writeValue(Traits, T)(T value) { 1331 if (is(T == typeof(null))) result ~= "null"; 1332 else { 1333 assert(isSupportedValueType!T); 1334 result ~= "V("~T.mangleof~")("~value.to!string~")"; 1335 } 1336 } 1337 1338 // deserialization 1339 void readDictionary(Traits)(scope void delegate(string) @safe entry_callback) 1340 { 1341 skip("D("~Traits.Type.mangleof~"){"); 1342 while (result.startsWith("DE(")) { 1343 result = result[3 .. $]; 1344 auto idx = result.indexOf(','); 1345 auto idx2 = result.indexOf(")("); 1346 assert(idx > 0 && idx2 > idx); 1347 auto t = result[0 .. idx]; 1348 auto n = result[idx+1 .. idx2]; 1349 result = result[idx2+2 .. $]; 1350 entry_callback(n); 1351 skip(")DE("~t~","~n~")"); 1352 } 1353 skip("}D("~Traits.Type.mangleof~")"); 1354 } 1355 1356 void beginReadDictionaryEntry(Traits)(string name) {} 1357 void endReadDictionaryEntry(Traits)(string name) {} 1358 1359 void readArray(Traits)(scope void delegate(size_t) @safe size_callback, scope void delegate() @safe entry_callback) 1360 { 1361 skip("A("~Traits.Type.mangleof~")["); 1362 auto bidx = result.indexOf("]["); 1363 assert(bidx > 0); 1364 auto cnt = result[0 .. bidx].to!size_t; 1365 result = result[bidx+2 .. $]; 1366 1367 size_t i = 0; 1368 while (result.startsWith("AE(")) { 1369 result = result[3 .. $]; 1370 auto idx = result.indexOf(','); 1371 auto idx2 = result.indexOf(")("); 1372 assert(idx > 0 && idx2 > idx); 1373 auto t = result[0 .. idx]; 1374 auto n = result[idx+1 .. idx2]; 1375 result = result[idx2+2 .. $]; 1376 assert(n == i.to!string); 1377 entry_callback(); 1378 skip(")AE("~t~","~n~")"); 1379 i++; 1380 } 1381 skip("]A("~Traits.Type.mangleof~")"); 1382 1383 assert(i == cnt); 1384 } 1385 1386 void beginReadArrayEntry(Traits)(size_t index) {} 1387 void endReadArrayEntry(Traits)(size_t index) {} 1388 1389 T readValue(Traits, T)() 1390 { 1391 skip("V("~T.mangleof~")("); 1392 auto idx = result.indexOf(')'); 1393 assert(idx >= 0); 1394 auto ret = result[0 .. idx].to!T; 1395 result = result[idx+1 .. $]; 1396 return ret; 1397 } 1398 1399 void skip(string prefix) 1400 @safe { 1401 assert(result.startsWith(prefix), prefix ~ " vs. " ~result); 1402 result = result[prefix.length .. $]; 1403 } 1404 1405 bool tryReadNull(Traits)() 1406 { 1407 if (result.startsWith("null")) { 1408 result = result[4 .. $]; 1409 return true; 1410 } else return false; 1411 } 1412 } 1413 } 1414 1415 unittest { // basic serialization behavior 1416 import std.typecons : Nullable; 1417 1418 static void test(T)(T value, string expected) { 1419 assert(serialize!TestSerializer(value) == expected, serialize!TestSerializer(value)); 1420 static if (isPointer!T) { 1421 if (value) assert(*deserialize!(TestSerializer, T)(expected) == *value); 1422 else assert(deserialize!(TestSerializer, T)(expected) is null); 1423 } else static if (is(T == Nullable!U, U)) { 1424 if (value.isNull()) assert(deserialize!(TestSerializer, T)(expected).isNull); 1425 else assert(deserialize!(TestSerializer, T)(expected) == value); 1426 } else assert(deserialize!(TestSerializer, T)(expected) == value); 1427 } 1428 1429 test("hello", "V(Aya)(hello)"); 1430 test(12, "V(i)(12)"); 1431 test(12.0, "V(Aya)(12)"); 1432 test(12.0f, "V(f)(12)"); 1433 assert(serialize!TestSerializer(null) == "null"); 1434 test(["hello", "world"], "A(AAya)[2][AE(Aya,0)(V(Aya)(hello))AE(Aya,0)AE(Aya,1)(V(Aya)(world))AE(Aya,1)]A(AAya)"); 1435 string mangleOfAA = (string[string]).mangleof; 1436 test(["hello": "world"], "D(" ~ mangleOfAA ~ "){DE(Aya,hello)(V(Aya)(world))DE(Aya,hello)}D(" ~ mangleOfAA ~ ")"); 1437 test(cast(int*)null, "null"); 1438 int i = 42; 1439 test(&i, "V(i)(42)"); 1440 Nullable!int j; 1441 test(j, "null"); 1442 j = 42; 1443 test(j, "V(i)(42)"); 1444 } 1445 1446 unittest { // basic user defined types 1447 static struct S { string f; } 1448 enum Sm = S.mangleof; 1449 auto s = S("hello"); 1450 enum s_ser = "D("~Sm~"){DE(Aya,f)(V(Aya)(hello))DE(Aya,f)}D("~Sm~")"; 1451 assert(serialize!TestSerializer(s) == s_ser, serialize!TestSerializer(s)); 1452 assert(deserialize!(TestSerializer, S)(s_ser) == s); 1453 1454 static class C { string f; } 1455 enum Cm = C.mangleof; 1456 C c; 1457 assert(serialize!TestSerializer(c) == "null"); 1458 c = new C; 1459 c.f = "hello"; 1460 enum c_ser = "D("~Cm~"){DE(Aya,f)(V(Aya)(hello))DE(Aya,f)}D("~Cm~")"; 1461 assert(serialize!TestSerializer(c) == c_ser); 1462 assert(deserialize!(TestSerializer, C)(c_ser).f == c.f); 1463 1464 enum E { hello, world } 1465 assert(serialize!TestSerializer(E.hello) == "V(i)(0)"); 1466 assert(serialize!TestSerializer(E.world) == "V(i)(1)"); 1467 } 1468 1469 unittest { // tuple serialization 1470 import std.typecons : Tuple; 1471 1472 static struct S(T...) { T f; } 1473 enum Sm = S!(int, string).mangleof; 1474 enum Tum = Tuple!(int, string).mangleof; 1475 const s = S!(int, string)(42, "hello"); 1476 1477 const ss = serialize!TestSerializer(s); 1478 const es = "D("~Sm~"){DE("~Tum~",f)(A("~Tum~")[2][AE(i,0)(V(i)(42))AE(i,0)AE(Aya,1)(V(Aya)(hello))AE(Aya,1)]A("~Tum~"))DE("~Tum~",f)}D("~Sm~")"; 1479 assert(ss == es); 1480 1481 const dss = deserialize!(TestSerializer, typeof(s))(ss); 1482 assert(dss == s); 1483 1484 static struct T { @asArray S!(int, string) g; } 1485 enum Tm = T.mangleof; 1486 const t = T(s); 1487 1488 const st = serialize!TestSerializer(t); 1489 const et = "D("~Tm~"){DE("~Sm~",g)(A("~Sm~")[2][AE(i,0)(V(i)(42))AE(i,0)AE(Aya,1)(V(Aya)(hello))AE(Aya,1)]A("~Sm~"))DE("~Sm~",g)}D("~Tm~")"; 1490 assert(st == et); 1491 1492 const dst = deserialize!(TestSerializer, typeof(t))(st); 1493 assert(dst == t); 1494 } 1495 1496 unittest { // named tuple serialization 1497 import std.typecons : tuple; 1498 1499 static struct I { 1500 int i; 1501 } 1502 1503 static struct S { 1504 int x; 1505 string s_; 1506 } 1507 1508 static struct T { 1509 @asArray 1510 typeof(tuple!(FieldNameTuple!I)(I.init.tupleof)) tuple1AsArray; 1511 1512 @name(fullyQualifiedName!I) 1513 typeof(tuple!(FieldNameTuple!I)(I.init.tupleof)) tuple1AsDictionary; 1514 1515 @asArray 1516 typeof(tuple!(FieldNameTuple!S)(S.init.tupleof)) tuple2AsArray; 1517 1518 @name(fullyQualifiedName!S) 1519 typeof(tuple!(FieldNameTuple!S)(S.init.tupleof)) tuple2AsDictionary; 1520 } 1521 1522 const i = I(42); 1523 const s = S(42, "hello"); 1524 const T t = { i.tupleof, i.tupleof, s.tupleof, s.tupleof }; 1525 1526 const st = serialize!TestSerializer(t); 1527 1528 enum Tm = T.mangleof; 1529 enum TuIm = typeof(T.tuple1AsArray).mangleof; 1530 enum TuSm = typeof(T.tuple2AsArray).mangleof; 1531 1532 const et = 1533 "D("~Tm~")"~ 1534 "{"~ 1535 "DE("~TuIm~",tuple1AsArray)"~ 1536 "("~ 1537 "V(i)(42)"~ 1538 ")"~ 1539 "DE("~TuIm~",tuple1AsArray)"~ 1540 "DE("~TuIm~","~fullyQualifiedName!I~")"~ 1541 "("~ 1542 "D("~TuIm~")"~ 1543 "{"~ 1544 "DE(i,i)"~ 1545 "("~ 1546 "V(i)(42)"~ 1547 ")"~ 1548 "DE(i,i)"~ 1549 "}"~ 1550 "D("~TuIm~")"~ 1551 ")"~ 1552 "DE("~TuIm~","~fullyQualifiedName!I~")"~ 1553 "DE("~TuSm~",tuple2AsArray)"~ 1554 "("~ 1555 "A("~TuSm~")[2]"~ 1556 "["~ 1557 "AE(i,0)"~ 1558 "("~ 1559 "V(i)(42)"~ 1560 ")"~ 1561 "AE(i,0)"~ 1562 "AE(Aya,1)"~ 1563 "("~ 1564 "V(Aya)(hello)"~ 1565 ")"~ 1566 "AE(Aya,1)"~ 1567 "]"~ 1568 "A("~TuSm~")"~ 1569 ")"~ 1570 "DE("~TuSm~",tuple2AsArray)"~ 1571 "DE("~TuSm~","~fullyQualifiedName!S~")"~ 1572 "("~ 1573 "D("~TuSm~")"~ 1574 "{"~ 1575 "DE(i,x)"~ 1576 "("~ 1577 "V(i)(42)"~ 1578 ")"~ 1579 "DE(i,x)"~ 1580 "DE(Aya,s)"~ 1581 "("~ 1582 "V(Aya)(hello)"~ 1583 ")"~ 1584 "DE(Aya,s)"~ 1585 "}"~ 1586 "D("~TuSm~")"~ 1587 ")"~ 1588 "DE("~TuSm~","~fullyQualifiedName!S~")"~ 1589 "}"~ 1590 "D("~Tm~")"; 1591 assert(st == et); 1592 1593 const dst = deserialize!(TestSerializer, typeof(t))(st); 1594 assert(dst == t); 1595 } 1596 1597 unittest { // testing the various UDAs 1598 enum E { hello, world } 1599 enum Em = E.mangleof; 1600 static struct S { 1601 @byName E e; 1602 @ignore int i; 1603 @optional float f; 1604 } 1605 enum Sm = S.mangleof; 1606 auto s = S(E.world, 42, 1.0f); 1607 assert(serialize!TestSerializer(s) == 1608 "D("~Sm~"){DE("~Em~",e)(V(Aya)(world))DE("~Em~",e)DE(f,f)(V(f)(1))DE(f,f)}D("~Sm~")"); 1609 } 1610 1611 unittest { // custom serialization support 1612 // iso-ext 1613 import std.datetime; 1614 auto t = TimeOfDay(6, 31, 23); 1615 assert(serialize!TestSerializer(t) == "V(Aya)(06:31:23)"); 1616 auto d = Date(1964, 1, 23); 1617 assert(serialize!TestSerializer(d) == "V(Aya)(1964-01-23)"); 1618 auto dt = DateTime(d, t); 1619 assert(serialize!TestSerializer(dt) == "V(Aya)(1964-01-23T06:31:23)"); 1620 auto st = SysTime(dt, UTC()); 1621 assert(serialize!TestSerializer(st) == "V(Aya)(1964-01-23T06:31:23Z)"); 1622 } 1623 1624 @safe unittest { // custom serialization support 1625 // string 1626 static struct S1 { int i; string toString() const @safe { return "hello"; } static S1 fromString(string) @safe { return S1.init; } } 1627 static struct S2 { int i; string toString() const { return "hello"; } } 1628 enum S2m = S2.mangleof; 1629 static struct S3 { int i; static S3 fromString(string) { return S3.init; } } 1630 enum S3m = S3.mangleof; 1631 assert(serialize!TestSerializer(S1.init) == "V(Aya)(hello)"); 1632 assert(serialize!TestSerializer(S2.init) == "D("~S2m~"){DE(i,i)(V(i)(0))DE(i,i)}D("~S2m~")"); 1633 assert(serialize!TestSerializer(S3.init) == "D("~S3m~"){DE(i,i)(V(i)(0))DE(i,i)}D("~S3m~")"); 1634 1635 // custom 1636 static struct C1 { int i; float toRepresentation() const @safe { return 1.0f; } static C1 fromRepresentation(float f) @safe { return C1.init; } } 1637 static struct C2 { int i; float toRepresentation() const { return 1.0f; } } 1638 enum C2m = C2.mangleof; 1639 static struct C3 { int i; static C3 fromRepresentation(float f) { return C3.init; } } 1640 enum C3m = C3.mangleof; 1641 assert(serialize!TestSerializer(C1.init) == "V(f)(1)"); 1642 assert(serialize!TestSerializer(C2.init) == "D("~C2m~"){DE(i,i)(V(i)(0))DE(i,i)}D("~C2m~")"); 1643 assert(serialize!TestSerializer(C3.init) == "D("~C3m~"){DE(i,i)(V(i)(0))DE(i,i)}D("~C3m~")"); 1644 } 1645 1646 unittest // Testing corner case: member function returning by ref 1647 { 1648 import vibe.data.json; 1649 1650 static struct S 1651 { 1652 int i; 1653 ref int foo() { return i; } 1654 } 1655 1656 static assert(__traits(compiles, { S().serializeToJson(); })); 1657 static assert(__traits(compiles, { Json().deserializeJson!S(); })); 1658 1659 auto s = S(1); 1660 assert(s.serializeToJson().deserializeJson!S() == s); 1661 } 1662 1663 unittest // Testing corner case: Variadic template constructors and methods 1664 { 1665 import vibe.data.json; 1666 1667 static struct S 1668 { 1669 int i; 1670 this(Args...)(Args args) {} 1671 int foo(Args...)(Args args) { return i; } 1672 ref int bar(Args...)(Args args) { return i; } 1673 } 1674 1675 static assert(__traits(compiles, { S().serializeToJson(); })); 1676 static assert(__traits(compiles, { Json().deserializeJson!S(); })); 1677 1678 auto s = S(1); 1679 assert(s.serializeToJson().deserializeJson!S() == s); 1680 } 1681 1682 @safe unittest // Make sure serializing through properties still works 1683 { 1684 import vibe.data.json; 1685 1686 static struct S 1687 { 1688 @safe: 1689 public int i; 1690 private int privateJ; 1691 1692 @property int j() @safe { return privateJ; } 1693 @property void j(int j) @safe { privateJ = j; } 1694 } 1695 1696 auto s = S(1, 2); 1697 assert(s.serializeToJson().deserializeJson!S() == s); 1698 } 1699 1700 @safe unittest // Immutable data deserialization 1701 { 1702 import vibe.data.json; 1703 1704 static struct S { 1705 int a; 1706 } 1707 static class C { 1708 immutable(S)[] arr; 1709 } 1710 1711 auto c = new C; 1712 c.arr ~= S(10); 1713 auto d = c.serializeToJson().deserializeJson!(immutable C); 1714 static assert(is(typeof(d) == immutable C)); 1715 assert(d.arr == c.arr); 1716 } 1717 1718 unittest { // test BitFlags serialization 1719 import std.typecons : BitFlags; 1720 1721 enum Flag { 1722 a = 1<<0, 1723 b = 1<<1, 1724 c = 1<<2 1725 } 1726 enum Flagm = Flag.mangleof; 1727 1728 alias Flags = BitFlags!Flag; 1729 enum Flagsm = Flags.mangleof; 1730 1731 enum Fi_ser = "A("~Flagsm~")[0][]A("~Flagsm~")"; 1732 assert(serialize!TestSerializer(Flags.init) == Fi_ser); 1733 1734 enum Fac_ser = "A("~Flagsm~")[2][AE("~Flagm~",0)(V(i)(1))AE("~Flagm~",0)AE("~Flagm~",1)(V(i)(4))AE("~Flagm~",1)]A("~Flagsm~")"; 1735 assert(serialize!TestSerializer(Flags(Flag.a, Flag.c)) == Fac_ser); 1736 1737 struct S { @byName Flags f; } 1738 enum Sm = S.mangleof; 1739 enum Sac_ser = "D("~Sm~"){DE("~Flagsm~",f)(A("~Flagsm~")[2][AE("~Flagm~",0)(V(Aya)(a))AE("~Flagm~",0)AE("~Flagm~",1)(V(Aya)(c))AE("~Flagm~",1)]A("~Flagsm~"))DE("~Flagsm~",f)}D("~Sm~")"; 1740 1741 assert(serialize!TestSerializer(S(Flags(Flag.a, Flag.c))) == Sac_ser); 1742 1743 assert(deserialize!(TestSerializer, Flags)(Fi_ser) == Flags.init); 1744 assert(deserialize!(TestSerializer, Flags)(Fac_ser) == Flags(Flag.a, Flag.c)); 1745 assert(deserialize!(TestSerializer, S)(Sac_ser) == S(Flags(Flag.a, Flag.c))); 1746 } 1747 1748 @safe unittest { // issue #1182 1749 struct T { 1750 int x; 1751 string y; 1752 } 1753 struct S { 1754 @asArray T t; 1755 } 1756 1757 auto s = S(T(42, "foo")); 1758 enum Sm = S.mangleof; 1759 enum Tm = T.mangleof; 1760 enum s_ser = "D("~Sm~"){DE("~Tm~",t)(A("~Tm~")[2][AE(i,0)(V(i)(42))AE(i,0)AE(Aya,1)(V(Aya)(foo))AE(Aya,1)]A("~Tm~"))DE("~Tm~",t)}D("~Sm~")"; 1761 1762 auto serialized = serialize!TestSerializer(s); 1763 assert(serialized == s_ser, serialized); 1764 assert(deserialize!(TestSerializer, S)(serialized) == s); 1765 } 1766 1767 @safe unittest { // issue #1352 - ingore per policy 1768 struct P1 {} 1769 struct P2 {} 1770 1771 struct T { 1772 @ignore int a = 5; 1773 @ignore!P1 @ignore!P2 int b = 6; 1774 @ignore!P1 c = 7; 1775 int d = 8; 1776 } 1777 1778 auto t = T(1, 2, 3, 4); 1779 auto Tm = T.mangleof; 1780 auto t_ser_plain = "D("~Tm~"){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~")"; 1781 auto t_ser_p1 = "D("~Tm~"){DE(i,d)(V(i)(4))DE(i,d)}D("~Tm~")"; 1782 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~")"; 1783 1784 { 1785 auto serialized_plain = serialize!TestSerializer(t); 1786 assert(serialized_plain == t_ser_plain); 1787 assert(deserialize!(TestSerializer, T)(serialized_plain) == T(5, 2, 3, 4)); 1788 } 1789 1790 { 1791 auto serialized_p1 = serializeWithPolicy!(TestSerializer, P1)(t); 1792 assert(serialized_p1 == t_ser_p1, serialized_p1); 1793 assert(deserializeWithPolicy!(TestSerializer, P1, T)(serialized_p1) == T(5, 6, 7, 4)); 1794 } 1795 1796 { 1797 auto serialized_p2 = serializeWithPolicy!(TestSerializer, P2)(t); 1798 assert(serialized_p2 == t_ser_p2); 1799 assert(deserializeWithPolicy!(TestSerializer, P2, T)(serialized_p2) == T(5, 6, 3, 4)); 1800 } 1801 } 1802 1803 unittest { 1804 import std.conv : to; 1805 import std.string : toLower, toUpper; 1806 1807 template P(T) if (is(T == enum)) { 1808 @safe: 1809 static string toRepresentation(T v) { return v.to!string.toLower(); } 1810 static T fromRepresentation(string str) { return str.toUpper().to!T; } 1811 } 1812 1813 1814 enum E { 1815 RED, 1816 GREEN 1817 } 1818 1819 assert(P!E.fromRepresentation("green") == E.GREEN); 1820 static assert(isPolicySerializable!(P, E)); 1821 1822 auto ser_red = "V(Aya)(red)"; 1823 assert(serializeWithPolicy!(TestSerializer, P)(E.RED) == ser_red, serializeWithPolicy!(TestSerializer, P)(E.RED)); 1824 assert(deserializeWithPolicy!(TestSerializer, P, E)(ser_red) == E.RED); 1825 1826 import vibe.data.json : Json, JsonSerializer; 1827 assert(serializeWithPolicy!(JsonSerializer, P)(E.RED) == Json("red")); 1828 } 1829 1830 unittest { 1831 static struct R { int y; } 1832 static struct Custom { 1833 @safe: 1834 int x; 1835 R toRepresentation() const { return R(x); } 1836 static Custom fromRepresentation(R r) { return Custom(r.y); } 1837 } 1838 1839 auto c = Custom(42); 1840 auto Rn = R.mangleof; 1841 auto ser = serialize!TestSerializer(c); 1842 assert(ser == "D("~Rn~"){DE(i,y)(V(i)(42))DE(i,y)}D("~Rn~")"); 1843 auto deser = deserialize!(TestSerializer, Custom)(ser); 1844 assert(deser.x == 42); 1845 } 1846 1847 unittest { 1848 import std.typecons : Typedef; 1849 alias T = Typedef!int; 1850 auto ser = serialize!TestSerializer(T(42)); 1851 assert(ser == "V(i)(42)", ser); 1852 auto deser = deserialize!(TestSerializer, T)(ser); 1853 assert(deser == 42); 1854 } 1855 1856 @safe unittest { 1857 static struct Foo { Foo[] foos; } 1858 Foo f; 1859 string ser = serialize!TestSerializer(f); 1860 assert(deserialize!(TestSerializer, Foo)(ser) == f); 1861 } 1862 1863 @system unittest { 1864 static struct SystemSerializer { 1865 TestSerializer ser; 1866 alias ser this; 1867 this(string s) { ser.result = s; } 1868 T readValue(Traits, T)() @system { return ser.readValue!(Traits, T); } 1869 void writeValue(Traits, T)(T value) @system { ser.writeValue!(Traits, T)(value); } 1870 void readDictionary(Traits)(scope void delegate(string) @system entry_callback) { ser.readDictionary!Traits((s) @trusted { entry_callback(s); }); } 1871 void readArray(Traits)(scope void delegate(size_t) @system size_callback, scope void delegate() @system entry_callback) { ser.readArray!Traits((s) @trusted { size_callback(s); }, () @trusted { entry_callback(); }); } 1872 } 1873 1874 static struct Bar { Bar[] foos; int i; } 1875 Bar f; 1876 string ser = serialize!SystemSerializer(f); 1877 assert(deserialize!(SystemSerializer, Bar)(ser) == f); 1878 } 1879 1880 @safe unittest { 1881 static struct S { @name("+foo") int bar; } 1882 auto Sn = S.mangleof; 1883 auto s = S(42); 1884 string ser = serialize!TestSerializer(s); 1885 assert(ser == "D("~Sn~"){DE(i,+foo)(V(i)(42))DE(i,+foo)}D("~Sn~")", ser); 1886 auto deser = deserialize!(TestSerializer, S)(ser); 1887 assert(deser.bar == 42); 1888 } 1889 1890 @safe unittest { 1891 static struct S { int bar_; } 1892 auto Sn = S.mangleof; 1893 auto s = S(42); 1894 string ser = serialize!TestSerializer(s); 1895 assert(ser == "D("~Sn~"){DE(i,bar)(V(i)(42))DE(i,bar)}D("~Sn~")", ser); 1896 auto deser = deserialize!(TestSerializer, S)(ser); 1897 assert(deser.bar_ == 42); 1898 }