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