1 /++ 2 $(H3 FGHJ and JSON Serialization) 3 4 For aggregate types the order of the (de)serialization is the folowing: 5 1. All public fields of `alias ? this` that are not hidden by members of `this` (recursively). 6 2. All public fields of `this`. 7 3. All public properties of `alias ? this` that are not hidden by members of `this` (recursively). 8 4. All public properties of `this`. 9 10 Publicly imports `mir.serde` from the `mir-algorithm` package. 11 +/ 12 module fghj.serialization; 13 14 import fghj.jsonparser: assumePure; 15 import mir.algebraic: isVariant; 16 import mir.reflection; 17 import std.range.primitives: isOutputRange; 18 public import mir.serde; 19 20 /// 21 pure 22 unittest 23 { 24 import fghj; 25 import std.bigint; 26 import std.datetime; 27 import mir.conv; 28 29 enum E : char 30 { 31 a, 32 b, 33 c, 34 } 35 36 static class C 37 { 38 private double _foo; 39 pure: 40 this() 41 { 42 _foo = 4; 43 } 44 45 double foo() const @property 46 { 47 return _foo + 10; 48 } 49 50 void foo(double d) @property 51 { 52 _foo = d - 10; 53 } 54 } 55 56 import mir.timestamp: Timestamp; 57 58 static struct S 59 { 60 static int staticNotSeialised = 5; 61 enum int enumsNotSeialised = 3; 62 63 @serdeProxy!Timestamp 64 DateTime time; 65 66 C object; 67 68 string[E] map; 69 70 @serdeKeys("bar_common", "bar") 71 string bar; 72 } 73 74 enum json = `{"time":"2016-03-04T00:00:00Z","object":{"foo":14.0},"map":{"a":"A"},"bar_common":"escaped chars = '\\', '\"', '\t', '\r', '\n'"}`; 75 auto value = S( 76 DateTime(2016, 3, 4), 77 new C, 78 [E.a : "A"], 79 "escaped chars = '\\', '\"', '\t', '\r', '\n'"); 80 assert(serializeToJson(cast(const)value) == json, serializeToJson(cast(const)value)); // check serialization of const data 81 assert(serializeToFghj(value).to!string == json, serializeToFghj(value).to!string); 82 assert(deserialize!S(json).serializeToJson == json); 83 } 84 85 /// `finalizeSerialization` method 86 unittest 87 { 88 import fghj; 89 90 static struct S 91 { 92 string a; 93 int b; 94 95 void finalizeSerialization(Serializer)(ref Serializer serializer) 96 { 97 serializer.putKey("c"); 98 serializer.putValue(100); 99 } 100 } 101 assert(S("bar", 3).serializeToJson == `{"a":"bar","b":3,"c":100}`); 102 } 103 104 /// `finalizeDeserialization` method 105 pure unittest 106 { 107 import fghj; 108 109 static struct S 110 { 111 string a; 112 int b; 113 114 @serdeIgnoreIn 115 double sum; 116 117 void finalizeDeserialization(Fghj data) pure 118 { 119 auto r = data["c", "d"]; 120 auto a = r["e"].get(0.0); 121 auto b = r["g"].get(0.0); 122 sum = a + b; 123 } 124 125 void serdeFinalize() pure 126 { 127 sum *= 2; 128 } 129 } 130 assert(`{"a":"bar","b":3,"c":{"d":{"e":6,"g":7}}}`.deserialize!S == S("bar", 3, 26)); 131 } 132 133 /// A user may define setter and/or getter properties. 134 unittest 135 { 136 import fghj; 137 import mir.conv: to; 138 139 static struct S 140 { 141 @serdeIgnore string str; 142 pure: 143 string a() @property 144 { 145 return str; 146 } 147 148 void b(int s) @property 149 { 150 str = s.to!string; 151 } 152 } 153 154 assert(S("str").serializeToJson == `{"a":"str"}`); 155 assert(`{"b":123}`.deserialize!S.str == "123"); 156 } 157 158 /// Support for custom nullable types (types that has a bool property `isNull`, 159 /// non-void property `get` returning payload and void property `nullify` that 160 /// makes nullable type to null value) 161 unittest 162 { 163 import fghj; 164 165 static struct MyNullable 166 { 167 long value; 168 169 @property 170 isNull() const 171 { 172 return value == 0; 173 } 174 175 @property 176 get() 177 { 178 return value; 179 } 180 181 @property 182 nullify() 183 { 184 value = 0; 185 } 186 187 auto opAssign(long value) 188 { 189 this.value = value; 190 } 191 } 192 193 static struct Foo 194 { 195 MyNullable my_nullable; 196 string field; 197 198 bool opEquals()(auto ref const(typeof(this)) rhs) 199 { 200 if (my_nullable.isNull && rhs.my_nullable.isNull) 201 return field == rhs.field; 202 203 if (my_nullable.isNull != rhs.my_nullable.isNull) 204 return false; 205 206 return my_nullable == rhs.my_nullable && 207 field == rhs.field; 208 } 209 } 210 211 Foo foo; 212 foo.field = "it's a foo"; 213 214 assert (serializeToJson(foo) == `{"my_nullable":null,"field":"it's a foo"}`); 215 216 foo.my_nullable = 200; 217 218 assert (deserialize!Foo(`{"my_nullable":200,"field":"it's a foo"}`) == Foo(MyNullable(200), "it's a foo")); 219 220 import std.typecons : Nullable; 221 222 static struct Bar 223 { 224 Nullable!long nullable; 225 string field; 226 227 bool opEquals()(auto ref const(typeof(this)) rhs) 228 { 229 if (nullable.isNull && rhs.nullable.isNull) 230 return field == rhs.field; 231 232 if (nullable.isNull != rhs.nullable.isNull) 233 return false; 234 235 return nullable == rhs.nullable && 236 field == rhs.field; 237 } 238 } 239 240 Bar bar; 241 bar.field = "it's a bar"; 242 243 assert (serializeToJson(bar) == `{"nullable":null,"field":"it's a bar"}`); 244 245 bar.nullable = 777; 246 assert (deserialize!Bar(`{"nullable":777,"field":"it's a bar"}`) == Bar(Nullable!long(777), "it's a bar")); 247 248 static struct S 249 { 250 long i; 251 252 SerdeException deserializeFromFghj(Fghj data) 253 { 254 if (auto exc = deserializeValue(data, i)) 255 return exc; 256 return null; 257 } 258 } 259 260 static struct T 261 { 262 // import std.typecons: Nullable; 263 import mir.algebraic: Nullable; 264 Nullable!S test; 265 } 266 T t = deserialize!T(`{ "test": 5 }`); 267 assert(t.test.i == 5); 268 } 269 270 271 // unittest 272 // { 273 // Fghj[string] map; 274 275 // map["num"] = serializeToFghj(124); 276 // map["str"] = serializeToFghj("value"); 277 278 // import std.stdio; 279 // map.serializeToJson.writeln(); 280 // } 281 282 /// Support for floating point nan and (partial) infinity 283 unittest 284 { 285 import mir.conv: to; 286 import fghj; 287 288 static struct Foo 289 { 290 float f; 291 292 bool opEquals()(auto ref const(typeof(this)) rhs) 293 { 294 import std.math : isNaN, approxEqual; 295 296 if (f.isNaN && rhs.f.isNaN) 297 return true; 298 299 return approxEqual(f, rhs.f); 300 } 301 } 302 303 // test for Not a Number 304 assert (serializeToJson(Foo()).to!string == `{"f":"nan"}`); 305 assert (serializeToFghj(Foo()).to!string == `{"f":"nan"}`); 306 307 assert (deserialize!Foo(`{"f":null}`) == Foo()); 308 assert (deserialize!Foo(`{"f":"nan"}`) == Foo()); 309 310 assert (serializeToJson(Foo(1f/0f)).to!string == `{"f":"inf"}`); 311 assert (serializeToFghj(Foo(1f/0f)).to!string == `{"f":"inf"}`); 312 assert (deserialize!Foo(`{"f":"inf"}`) == Foo( float.infinity)); 313 assert (deserialize!Foo(`{"f":"-inf"}`) == Foo(-float.infinity)); 314 315 assert (serializeToJson(Foo(-1f/0f)).to!string == `{"f":"-inf"}`); 316 assert (serializeToFghj(Foo(-1f/0f)).to!string == `{"f":"-inf"}`); 317 assert (deserialize!Foo(`{"f":"-inf"}`) == Foo(-float.infinity)); 318 } 319 320 import fghj.fghj; 321 import mir.conv; 322 import std.bigint: BigInt; 323 import std.format: FormatSpec, formatValue; 324 import std.functional; 325 import std.meta; 326 import std.range.primitives; 327 import std.traits; 328 import std.utf; 329 330 deprecated("use mir.serde: SerdeException instead") 331 alias DeserializationException = SerdeException; 332 333 private SerdeException unexpectedKind(string msg = "Unexpected FGHJ kind")(ubyte kind) 334 @safe pure nothrow @nogc 335 { 336 import mir.conv: to; 337 static immutable exc(Fghj.Kind kind) = new SerdeException(msg ~ " " ~ kind.to!string); 338 339 switch (kind) 340 { 341 foreach (member; EnumMembers!(Fghj.Kind)) 342 {case member: 343 return exc!member; 344 } 345 default: 346 static immutable ret = new SerdeException("Wrong encoding of FGHJ kind"); 347 return ret; 348 } 349 } 350 351 /// JSON serialization function. 352 string serializeToJson(V)(auto ref V value) 353 { 354 return serializeToJsonPretty!""(value); 355 } 356 357 /// 358 unittest 359 { 360 import fghj; 361 362 struct S 363 { 364 string foo; 365 uint bar; 366 } 367 368 assert(serializeToJson(S("str", 4)) == `{"foo":"str","bar":4}`); 369 } 370 371 372 /// JSON serialization function with pretty formatting. 373 string serializeToJsonPretty(string sep = "\t", V)(auto ref V value) 374 { 375 import std.array: appender; 376 import std.functional: forward; 377 378 auto app = appender!(char[]); 379 serializeToJsonPretty!sep(forward!value, app); 380 return cast(string) app.data; 381 } 382 383 /// 384 unittest 385 { 386 import fghj; 387 388 static struct S { int a; } 389 assert(S(4).serializeToJsonPretty == "{\n\t\"a\": 4\n}"); 390 } 391 392 /// JSON serialization function with pretty formatting and custom output range. 393 void serializeToJsonPretty(string sep = "\t", V, O)(auto ref V value, ref O output) 394 if(isOutputRange!(O, const(char)[])) 395 { 396 import std.range.primitives: put; 397 auto ser = jsonSerializer!sep((const(char)[] chars) => put(output, chars)); 398 ser.serializeValue(value); 399 ser.flush; 400 } 401 402 403 /// FGHJ serialization function 404 Fghj serializeToFghj(V)(auto ref V value, size_t initialLength = 32) 405 { 406 auto ser = fghjSerializer(initialLength); 407 ser.serializeValue(value); 408 ser.flush; 409 return ser.app.result; 410 } 411 412 /// 413 unittest 414 { 415 import fghj; 416 import mir.conv: to; 417 418 struct S 419 { 420 string foo; 421 uint bar; 422 } 423 424 assert(serializeToFghj(S("str", 4)).to!string == `{"foo":"str","bar":4}`); 425 } 426 427 /// Deserialization function 428 V deserialize(V)(Fghj data) 429 { 430 V value; 431 static if (is(V == class)) value = new V; 432 if (auto exc = deserializeValue(data, value)) 433 throw exc; 434 return value; 435 } 436 437 /// ditto 438 V deserialize(V)(in char[] str) 439 { 440 import fghj.jsonparser: parseJson; 441 import std.range: only; 442 return str.parseJson.deserialize!V; 443 } 444 445 /// 446 unittest 447 { 448 struct S 449 { 450 string foo; 451 uint bar; 452 } 453 454 assert(deserialize!S(`{"foo":"str","bar":4}`) == S("str", 4)); 455 } 456 457 /// Proxy for members 458 unittest 459 { 460 struct S 461 { 462 // const(char)[] doesn't reallocate FGHJ data. 463 @serdeProxy!(const(char)[]) 464 uint bar; 465 } 466 467 auto json = `{"bar":"4"}`; 468 assert(serializeToJson(S(4)) == json); 469 assert(deserialize!S(json) == S(4)); 470 } 471 472 version(unittest) private 473 { 474 @serdeProxy!ProxyE 475 enum E 476 { 477 none, 478 bar, 479 } 480 481 // const(char)[] doesn't reallocate FGHJ data. 482 @serdeProxy!(const(char)[]) 483 struct ProxyE 484 { 485 E e; 486 487 this(E e) 488 { 489 this.e = e; 490 } 491 492 this(in char[] str) 493 { 494 switch(str) 495 { 496 case "NONE": 497 case "NA": 498 case "N/A": 499 e = E.none; 500 break; 501 case "BAR": 502 case "BR": 503 e = E.bar; 504 break; 505 default: 506 throw new Exception("Unknown: " ~ cast(string)str); 507 } 508 } 509 510 string toString() const 511 { 512 if (e == E.none) 513 return "NONE"; 514 else 515 return "BAR"; 516 } 517 518 E opCast(T : E)() 519 { 520 return e; 521 } 522 } 523 524 unittest 525 { 526 assert(serializeToJson(E.bar) == `"BAR"`); 527 assert(`"N/A"`.deserialize!E == E.none); 528 assert(`"NA"`.deserialize!E == E.none); 529 } 530 } 531 532 /// 533 pure unittest 534 { 535 static struct S 536 { 537 @serdeKeys("b", "a") 538 string s; 539 } 540 assert(`{"a":"d"}`.deserialize!S.serializeToJson == `{"b":"d"}`); 541 } 542 543 /// 544 pure unittest 545 { 546 static struct S 547 { 548 @serdeKeys("a") 549 @serdeKeyOut("s") 550 string s; 551 } 552 assert(`{"a":"d"}`.deserialize!S.serializeToJson == `{"s":"d"}`); 553 } 554 555 /// 556 pure unittest 557 { 558 import std.exception; 559 struct S 560 { 561 string field; 562 } 563 assert(`{"field":"val"}`.deserialize!S.field == "val"); 564 assertThrown(`{"other":"val"}`.deserialize!S); 565 } 566 567 /// 568 unittest 569 { 570 import fghj; 571 572 static struct S 573 { 574 @serdeKeyOut("a") 575 string s; 576 } 577 assert(`{"s":"d"}`.deserialize!S.serializeToJson == `{"a":"d"}`); 578 } 579 580 /// 581 unittest 582 { 583 import fghj; 584 585 static struct S 586 { 587 @serdeIgnore 588 string s; 589 } 590 assert(`{"s":"d"}`.deserialize!S.s == null); 591 assert(S("d").serializeToJson == `{}`); 592 } 593 594 /// 595 unittest 596 { 597 import fghj; 598 599 static struct Decor 600 { 601 int candles; // 0 602 float fluff = float.infinity; // inf 603 } 604 605 static struct Cake 606 { 607 @serdeIgnoreDefault 608 string name = "Chocolate Cake"; 609 int slices = 8; 610 float flavor = 1; 611 @serdeIgnoreDefault 612 Decor dec = Decor(20); // { 20, inf } 613 } 614 615 assert(Cake("Normal Cake").serializeToJson == `{"name":"Normal Cake","slices":8,"flavor":1.0}`); 616 auto cake = Cake.init; 617 cake.dec = Decor.init; 618 assert(cake.serializeToJson == `{"slices":8,"flavor":1.0,"dec":{"candles":0,"fluff":"inf"}}`); 619 assert(cake.dec.serializeToJson == `{"candles":0,"fluff":"inf"}`); 620 621 static struct A 622 { 623 @serdeIgnoreDefault 624 string str = "Banana"; 625 int i = 1; 626 } 627 assert(A.init.serializeToJson == `{"i":1}`); 628 629 static struct S 630 { 631 @serdeIgnoreDefault 632 A a; 633 } 634 assert(S.init.serializeToJson == `{}`); 635 assert(S(A("Berry")).serializeToJson == `{"a":{"str":"Berry","i":1}}`); 636 637 static struct D 638 { 639 S s; 640 } 641 assert(D.init.serializeToJson == `{"s":{}}`); 642 assert(D(S(A("Berry"))).serializeToJson == `{"s":{"a":{"str":"Berry","i":1}}}`); 643 assert(D(S(A(null, 0))).serializeToJson == `{"s":{"a":{"str":null,"i":0}}}`); 644 645 static struct F 646 { 647 D d; 648 } 649 assert(F.init.serializeToJson == `{"d":{"s":{}}}`); 650 } 651 652 /// 653 unittest 654 { 655 import fghj; 656 657 static struct S 658 { 659 @serdeIgnoreIn 660 string s; 661 } 662 assert(`{"s":"d"}`.deserialize!S.s == null); 663 assert(S("d").serializeToJson == `{"s":"d"}`); 664 } 665 666 /// 667 unittest 668 { 669 static struct S 670 { 671 @serdeIgnoreOut 672 string s; 673 } 674 assert(`{"s":"d"}`.deserialize!S.s == "d"); 675 assert(S("d").serializeToJson == `{}`); 676 } 677 678 /// 679 unittest 680 { 681 import fghj; 682 683 static struct S 684 { 685 @serdeIgnoreOutIf!`a < 0` 686 int a; 687 } 688 689 assert(serializeToJson(S(3)) == `{"a":3}`, serializeToJson(S(3))); 690 assert(serializeToJson(S(-3)) == `{}`); 691 } 692 693 /// 694 unittest 695 { 696 import fghj; 697 698 import std.uuid; 699 700 static struct S 701 { 702 @serdeScoped 703 @serdeProxy!string 704 UUID id; 705 } 706 assert(`{"id":"8AB3060E-2cba-4f23-b74c-b52db3bdfb46"}`.deserialize!S.id 707 == UUID("8AB3060E-2cba-4f23-b74c-b52db3bdfb46")); 708 } 709 710 /// Proxy type for array of algebraics 711 unittest 712 { 713 import fghj; 714 import mir.algebraic: Variant; 715 716 static struct ObjectA 717 { 718 string name; 719 } 720 static struct ObjectB 721 { 722 double value; 723 } 724 725 alias MyObject = Variant!(ObjectA, ObjectB); 726 727 static struct MyObjectArrayProxy 728 { 729 MyObject[] array; 730 731 this(MyObject[] array) @safe pure nothrow @nogc 732 { 733 this.array = array; 734 } 735 736 T opCast(T : MyObject[])() 737 { 738 return array; 739 } 740 741 void serialize(S)(ref S serializer) const 742 { 743 auto state = serializer.listBegin; 744 foreach (ref e; array) 745 { 746 serializer.elemBegin(); 747 // mir.algebraic has builtin support for serialization. 748 // For other algebraic libraies one can use thier visitor handlers. 749 serializeValue(serializer, e); 750 } 751 serializer.listEnd(state); 752 } 753 754 auto deserializeFromFghj(Fghj fghjData) 755 { 756 import fghj : deserializeValue; 757 import std.traits : EnumMembers; 758 759 foreach (e; fghjData.byElement) 760 { 761 if (e["name"] != Fghj.init) 762 { 763 array ~= MyObject(deserialize!ObjectA(e)); 764 } 765 else 766 { 767 array ~= MyObject(deserialize!ObjectB(e)); 768 } 769 } 770 771 return SerdeException.init; 772 } 773 } 774 775 static struct SomeObject 776 { 777 @serdeProxy!MyObjectArrayProxy MyObject[] objects; 778 } 779 780 string data = q{{"objects":[{"name":"test"},{"value":1.5}]}}; 781 782 auto value = data.deserialize!SomeObject; 783 assert (value.serializeToJson == data); 784 } 785 786 /// 787 unittest 788 { 789 import fghj; 790 791 import std.range; 792 import std.uuid; 793 794 static struct S 795 { 796 private int count; 797 @serdeLikeList 798 auto numbers() @property // uses `foreach` 799 { 800 return iota(count); 801 } 802 803 @serdeLikeList 804 @serdeProxy!string // input element type of 805 @serdeIgnoreOut 806 Appender!(string[]) strings; //`put` method is used 807 } 808 809 assert(S(5).serializeToJson == `{"numbers":[0,1,2,3,4]}`); 810 assert(`{"strings":["a","b"]}`.deserialize!S.strings.data == ["a","b"]); 811 } 812 813 /// 814 unittest 815 { 816 import fghj; 817 818 static struct M 819 { 820 private int sum; 821 822 // opApply is used for serialization 823 int opApply(int delegate(in char[] key, int val) pure dg) pure 824 { 825 if(auto r = dg("a", 1)) return r; 826 if(auto r = dg("b", 2)) return r; 827 if(auto r = dg("c", 3)) return r; 828 return 0; 829 } 830 831 // opIndexAssign for deserialization 832 void opIndexAssign(int val, string key) pure 833 { 834 sum += val; 835 } 836 } 837 838 static struct S 839 { 840 @serdeLikeStruct 841 @serdeProxy!int 842 M obj; 843 } 844 845 assert(S.init.serializeToJson == `{"obj":{"a":1,"b":2,"c":3}}`); 846 assert(`{"obj":{"a":1,"b":2,"c":9}}`.deserialize!S.obj.sum == 12); 847 } 848 849 /// 850 unittest 851 { 852 import fghj; 853 import std.range; 854 import std.algorithm; 855 import std.conv; 856 857 static struct S 858 { 859 @serdeTransformIn!"a += 2" 860 @serdeTransformOut!(a =>"str".repeat.take(a).joiner("_").to!string) 861 int a; 862 } 863 864 auto s = deserialize!S(`{"a":3}`); 865 assert(s.a == 5); 866 assert(serializeToJson(s) == `{"a":"str_str_str_str_str"}`); 867 } 868 869 /// JSON serialization back-end 870 struct JsonSerializer(string sep, Dg) 871 { 872 import fghj.jsonbuffer; 873 874 static if(sep.length) 875 { 876 private size_t deep; 877 878 private void putSpace() 879 { 880 for(auto k = deep; k; k--) 881 { 882 static if(sep.length == 1) 883 { 884 sink.put(sep[0]); 885 } 886 else 887 { 888 sink.put!sep; 889 } 890 } 891 } 892 } 893 894 895 /// JSON string buffer 896 JsonBuffer!Dg sink; 897 898 /// 899 this(Dg sink) 900 { 901 this.sink = JsonBuffer!Dg(sink); 902 } 903 904 private uint state; 905 906 private void pushState(uint state) 907 { 908 this.state = state; 909 } 910 911 private uint popState() 912 { 913 auto ret = state; 914 state = 0; 915 return ret; 916 } 917 918 private void incState() 919 { 920 if(state++) 921 { 922 static if(sep.length) 923 { 924 sink.put!",\n"; 925 } 926 else 927 { 928 sink.put(','); 929 } 930 } 931 } 932 933 /// Serialization primitives 934 uint structBegin(size_t length = 0) 935 { 936 static if(sep.length) 937 { 938 deep++; 939 sink.put!"{\n"; 940 } 941 else 942 { 943 sink.put('{'); 944 } 945 return popState; 946 } 947 948 ///ditto 949 void structEnd(uint state) 950 { 951 static if(sep.length) 952 { 953 deep--; 954 sink.put('\n'); 955 putSpace; 956 } 957 sink.put('}'); 958 pushState(state); 959 } 960 961 ///ditto 962 uint listBegin(size_t length = 0) 963 { 964 static if(sep.length) 965 { 966 deep++; 967 sink.put!"[\n"; 968 } 969 else 970 { 971 sink.put('['); 972 } 973 return popState; 974 } 975 976 ///ditto 977 void listEnd(uint state) 978 { 979 static if(sep.length) 980 { 981 deep--; 982 sink.put('\n'); 983 putSpace; 984 } 985 sink.put(']'); 986 pushState(state); 987 } 988 989 ///ditto 990 void putEscapedKey(in char[] key) 991 { 992 incState; 993 static if(sep.length) 994 { 995 putSpace; 996 } 997 sink.put('\"'); 998 sink.putSmallEscaped(key); 999 static if(sep.length) 1000 { 1001 sink.put!"\": "; 1002 } 1003 else 1004 { 1005 sink.put!"\":"; 1006 } 1007 } 1008 1009 ///ditto 1010 void putKey(in char[] key) 1011 { 1012 incState; 1013 static if(sep.length) 1014 { 1015 putSpace; 1016 } 1017 sink.put('\"'); 1018 sink.put(key); 1019 static if(sep.length) 1020 { 1021 sink.put!"\": "; 1022 } 1023 else 1024 { 1025 sink.put!"\":"; 1026 } 1027 } 1028 1029 ///ditto 1030 void putNumberValue(Num)(Num num, FormatSpec!char fmt = FormatSpec!char.init) 1031 { 1032 auto f = &sink.putSmallEscaped; 1033 static if (isNumeric!Num) 1034 { 1035 static struct S 1036 { 1037 typeof(f) fun; 1038 auto put(scope const(char)[] str) 1039 { 1040 fun(str); 1041 } 1042 } 1043 auto app = S(f); 1044 if (fmt == FormatSpec!char.init) 1045 { 1046 import mir.format: print; 1047 print(app, num); 1048 return; 1049 } 1050 } 1051 assumePure((typeof(f) fun) => formatValue(fun, num, fmt))(f); 1052 } 1053 1054 ///ditto 1055 void putValue(typeof(null)) 1056 { 1057 sink.put!"null"; 1058 } 1059 1060 ///ditto 1061 import mir.timestamp: Timestamp; 1062 void putValue(Timestamp timestamp) 1063 { 1064 import mir.format: stringBuf, getData; 1065 putValue(stringBuf() << timestamp << getData); 1066 } 1067 1068 ///ditto 1069 void putValue(bool b) 1070 { 1071 if(b) 1072 sink.put!"true"; 1073 else 1074 sink.put!"false"; 1075 } 1076 1077 ///ditto 1078 void putValue(in char[] str) 1079 { 1080 sink.put('\"'); 1081 sink.put(str); 1082 sink.put('\"'); 1083 } 1084 1085 ///ditto 1086 void putValue(Num)(Num num) 1087 if (isNumeric!Num && !is(Num == enum)) 1088 { 1089 putNumberValue(num); 1090 } 1091 1092 ///ditto 1093 void elemBegin() 1094 { 1095 incState; 1096 static if(sep.length) 1097 { 1098 putSpace; 1099 } 1100 } 1101 1102 ///ditto 1103 void flush() 1104 { 1105 sink.flush; 1106 } 1107 1108 deprecated("Use structBegin instead") alias objectBegin = structBegin; 1109 deprecated("Use structEnd instead") alias objectEnd = structEnd; 1110 deprecated("Use listBegin instead") alias arrayBegin = listBegin; 1111 deprecated("Use listEnd instead") alias arrayEnd = listEnd; 1112 } 1113 1114 /++ 1115 Creates JSON serialization back-end. 1116 Use `sep` equal to `"\t"` or `" "` for pretty formatting. 1117 +/ 1118 auto jsonSerializer(string sep = "", Dg)(scope Dg sink) 1119 { 1120 return JsonSerializer!(sep, Dg)(sink); 1121 } 1122 1123 /// 1124 unittest 1125 { 1126 import fghj; 1127 1128 import std.array; 1129 import std.bigint; 1130 import std.format: singleSpec; 1131 1132 auto app = appender!string; 1133 auto ser = jsonSerializer(&app.put!(const(char)[])); 1134 auto state0 = ser.structBegin; 1135 1136 ser.putEscapedKey("null"); 1137 ser.putValue(null); 1138 1139 ser.putEscapedKey("array"); 1140 auto state1 = ser.listBegin(); 1141 ser.elemBegin; ser.putValue(null); 1142 ser.elemBegin; ser.putValue(123); 1143 ser.elemBegin; ser.putNumberValue(12300000.123, singleSpec("%.10e")); 1144 ser.elemBegin; ser.putValue("\t"); 1145 ser.elemBegin; ser.putValue("\r"); 1146 ser.elemBegin; ser.putValue("\n"); 1147 ser.elemBegin; ser.putNumberValue(BigInt("1234567890")); 1148 ser.listEnd(state1); 1149 1150 ser.structEnd(state0); 1151 ser.flush; 1152 1153 assert(app.data == `{"null":null,"array":[null,123,1.2300000123e+07,"\t","\r","\n",1234567890]}`); 1154 } 1155 1156 unittest 1157 { 1158 import std.array; 1159 import std.bigint; 1160 import std.format: singleSpec; 1161 1162 auto app = appender!string; 1163 auto ser = jsonSerializer!" "(&app.put!(const(char)[])); 1164 auto state0 = ser.structBegin; 1165 1166 ser.putEscapedKey("null"); 1167 ser.putValue(null); 1168 1169 ser.putEscapedKey("array"); 1170 auto state1 = ser.listBegin(); 1171 ser.elemBegin; ser.putValue(null); 1172 ser.elemBegin; ser.putValue(123); 1173 ser.elemBegin; ser.putNumberValue(12300000.123, singleSpec("%.10e")); 1174 ser.elemBegin; ser.putValue("\t"); 1175 ser.elemBegin; ser.putValue("\r"); 1176 ser.elemBegin; ser.putValue("\n"); 1177 ser.elemBegin; ser.putNumberValue(BigInt("1234567890")); 1178 ser.listEnd(state1); 1179 1180 ser.structEnd(state0); 1181 ser.flush; 1182 1183 assert(app.data == 1184 `{ 1185 "null": null, 1186 "array": [ 1187 null, 1188 123, 1189 1.2300000123e+07, 1190 "\t", 1191 "\r", 1192 "\n", 1193 1234567890 1194 ] 1195 }`); 1196 } 1197 1198 /// FGHJ serialization back-end 1199 struct FghjSerializer 1200 { 1201 /// Output buffer 1202 OutputArray app; 1203 1204 import fghj.outputarray; 1205 import fghj.fghj; 1206 private uint state; 1207 1208 pure: 1209 1210 /// Serialization primitives 1211 size_t structBegin(size_t length = 0) 1212 { 1213 app.put1(Fghj.Kind.object); 1214 return app.skip(4); 1215 } 1216 1217 ///ditto 1218 void structEnd(size_t state) 1219 { 1220 app.put4(cast(uint)(app.shift - state - 4), state); 1221 } 1222 1223 ///ditto 1224 size_t listBegin(size_t length = 0) 1225 { 1226 app.put1(Fghj.Kind.array); 1227 return app.skip(4); 1228 } 1229 1230 ///ditto 1231 void listEnd(size_t state) 1232 { 1233 app.put4(cast(uint)(app.shift - state - 4), state); 1234 } 1235 1236 ///ditto 1237 alias putEscapedKey = putKey; 1238 1239 ///ditto 1240 void putKey(in char[] key) 1241 { 1242 auto sh = app.skip(1); 1243 app.put(key); 1244 app.put1(cast(ubyte)(app.shift - sh - 1), sh); 1245 } 1246 1247 ///ditto 1248 void putNumberValue(Num)(Num num, FormatSpec!char fmt = FormatSpec!char.init) pure 1249 { 1250 app.put1(Fghj.Kind.number); 1251 auto sh = app.skip(1); 1252 static if (isNumeric!Num) 1253 { 1254 if (fmt == FormatSpec!char.init) 1255 { 1256 import mir.format: print; 1257 print(app, num); 1258 app.put1(cast(ubyte)(app.shift - sh - 1), sh); 1259 return; 1260 } 1261 } 1262 assumePure((ref OutputArray app) => formatValue(app, num, fmt))(app); 1263 app.put1(cast(ubyte)(app.shift - sh - 1), sh); 1264 } 1265 1266 ///ditto 1267 void putValue(typeof(null)) 1268 { 1269 with(Fghj.Kind) app.put1(null_); 1270 } 1271 1272 ///ditto 1273 void putValue(bool b) 1274 { 1275 with(Fghj.Kind) app.put1(b ? true_ : false_); 1276 } 1277 1278 ///ditto 1279 import mir.timestamp: Timestamp; 1280 void putValue(Timestamp timestamp) 1281 { 1282 import mir.format: stringBuf, getData; 1283 putValue(stringBuf() << timestamp << getData); 1284 } 1285 1286 ///ditto 1287 void putValue(in char[] str) 1288 { 1289 app.put1(Fghj.Kind..string); 1290 auto sh = app.skip(4); 1291 app.put(str); 1292 app.put4(cast(uint)(app.shift - sh - 4), sh); 1293 } 1294 1295 ///ditto 1296 void putValue(Num)(Num num) 1297 if (isNumeric!Num && !is(Num == enum)) 1298 { 1299 putNumberValue(num); 1300 } 1301 1302 /// 1303 void putValue(Num)(const Num value) 1304 if (isNumeric!Num && !is(Num == enum)) 1305 { 1306 import mir.format: print; 1307 import mir.internal.utility: isFloatingPoint; 1308 1309 static if (isFloatingPoint!Num) 1310 { 1311 import mir.math.common: fabs; 1312 1313 if (value.fabs < value.infinity) 1314 print(app, value); 1315 else if (value == Num.infinity) 1316 app.put(`"+inf"`); 1317 else if (value == -Num.infinity) 1318 app.put(`"-inf"`); 1319 else 1320 app.put(`"nan"`); 1321 } 1322 else 1323 print(app, value); 1324 } 1325 1326 ///ditto 1327 static void elemBegin() 1328 { 1329 } 1330 1331 ///ditto 1332 static void flush() 1333 { 1334 } 1335 1336 deprecated("Use structBegin instead") alias objectBegin = structBegin; 1337 deprecated("Use structEnd instead") alias objectEnd = structEnd; 1338 deprecated("Use listBegin instead") alias arrayBegin = listBegin; 1339 deprecated("Use listEnd instead") alias arrayEnd = listEnd; 1340 } 1341 1342 /// Create FGHJ serialization back-end 1343 auto fghjSerializer(size_t initialLength = 32) 1344 { 1345 import fghj.outputarray; 1346 return FghjSerializer(OutputArray(initialLength)); 1347 } 1348 1349 /// 1350 unittest 1351 { 1352 import fghj; 1353 import mir.conv: to; 1354 import std.bigint; 1355 import std.format: singleSpec; 1356 1357 auto ser = fghjSerializer(); 1358 auto state0 = ser.structBegin; 1359 1360 ser.putEscapedKey("null"); 1361 ser.putValue(null); 1362 1363 ser.putKey("array"); 1364 auto state1 = ser.listBegin(); 1365 ser.elemBegin; ser.putValue(null); 1366 ser.elemBegin; ser.putValue(123); 1367 ser.elemBegin; ser.putNumberValue(12300000.123, singleSpec("%.10e")); 1368 ser.elemBegin; ser.putValue("\t"); 1369 ser.elemBegin; ser.putValue("\r"); 1370 ser.elemBegin; ser.putValue("\n"); 1371 ser.elemBegin; ser.putNumberValue(BigInt("1234567890")); 1372 ser.listEnd(state1); 1373 1374 ser.structEnd(state0); 1375 1376 assert(ser.app.result.to!string == `{"null":null,"array":[null,123,1.2300000123e+07,"\t","\r","\n",1234567890]}`); 1377 } 1378 1379 /// `null` value serialization 1380 void serializeValue(S)(ref S serializer, typeof(null)) 1381 { 1382 serializer.putValue(null); 1383 } 1384 1385 /// 1386 unittest 1387 { 1388 import fghj; 1389 1390 assert(serializeToJson(null) == `null`); 1391 } 1392 1393 /// Number serialization 1394 void serializeValue(S, V)(ref S serializer, in V value, FormatSpec!char fmt = FormatSpec!char.init) 1395 if((isNumeric!V && !is(V == enum)) || is(V == BigInt)) 1396 { 1397 static if (isFloatingPoint!V) 1398 { 1399 import std.math : isNaN, isFinite, signbit; 1400 1401 if (isFinite(value)) 1402 serializer.putNumberValue(value, fmt); 1403 else if (value.isNaN) 1404 serializer.putValue(signbit(value) ? "-nan" : "nan"); 1405 else if (value == V.infinity) 1406 serializer.putValue("inf"); 1407 else if (value == -V.infinity) 1408 serializer.putValue("-inf"); 1409 } 1410 else 1411 serializer.putNumberValue(value, fmt); 1412 } 1413 1414 /// 1415 unittest 1416 { 1417 import std.bigint; 1418 1419 assert(serializeToJson(BigInt(123)) == `123`); 1420 assert(serializeToJson(2.40f) == `2.4`); 1421 assert(serializeToJson(float.nan) == `"nan"`); 1422 assert(serializeToJson(float.infinity) == `"inf"`); 1423 assert(serializeToJson(-float.infinity) == `"-inf"`); 1424 } 1425 1426 /// Boolean serialization 1427 void serializeValue(S, V)(ref S serializer, const V value) 1428 if (is(V == bool) && !is(V == enum)) 1429 { 1430 serializer.putValue(value); 1431 } 1432 1433 /// Char serialization 1434 void serializeValue(S, V : char)(ref S serializer, const V value) 1435 if (is(V == char) && !is(V == enum)) 1436 { 1437 auto v = cast(char[1])value; 1438 serializer.putValue(v[]); 1439 } 1440 1441 /// 1442 unittest 1443 { 1444 assert(serializeToJson(true) == `true`); 1445 } 1446 1447 /// Enum serialization 1448 void serializeValue(S, V)(ref S serializer, in V value) 1449 if(is(V == enum)) 1450 { 1451 static if (hasUDA!(V, serdeProxy)) 1452 { 1453 serializer.serializeValue(value.to!(serdeGetProxy!V)); 1454 } 1455 else 1456 { 1457 serializer.putValue(serdeGetKeyOut(value)); 1458 } 1459 } 1460 1461 /// 1462 unittest 1463 { 1464 enum Key { @serdeKeys("FOO", "foo") foo } 1465 assert(serializeToJson(Key.foo) == `"FOO"`); 1466 } 1467 1468 /// String serialization 1469 void serializeValue(S)(ref S serializer, in char[] value) 1470 { 1471 if(value is null) 1472 { 1473 serializer.putValue(null); 1474 return; 1475 } 1476 serializer.putValue(value); 1477 } 1478 1479 /// 1480 unittest 1481 { 1482 assert(serializeToJson("\t \" \\") == `"\t \" \\"`); 1483 } 1484 1485 /// Array serialization 1486 void serializeValue(S, T)(ref S serializer, T[] value) 1487 if(!isSomeChar!T) 1488 { 1489 if(value is null) 1490 { 1491 serializer.putValue(null); 1492 return; 1493 } 1494 auto state = serializer.listBegin(); 1495 foreach (ref elem; value) 1496 { 1497 serializer.elemBegin; 1498 serializer.serializeValue(elem); 1499 } 1500 serializer.listEnd(state); 1501 } 1502 1503 /// Input range serialization 1504 void serializeValue(S, R)(ref S serializer, R value) 1505 if ((isInputRange!R) && 1506 !isSomeChar!(ElementType!R) && 1507 !isDynamicArray!R && 1508 !isStdNullable!R) 1509 { 1510 auto state = serializer.listBegin(); 1511 foreach (ref elem; value) 1512 { 1513 serializer.elemBegin; 1514 serializer.serializeValue(elem); 1515 } 1516 serializer.listEnd(state); 1517 } 1518 1519 /// input range serialization 1520 unittest 1521 { 1522 import std.algorithm : filter; 1523 1524 struct Foo 1525 { 1526 int i; 1527 } 1528 1529 auto ar = [Foo(1), Foo(3), Foo(4), Foo(17)]; 1530 1531 auto filtered1 = ar.filter!"a.i & 1"; 1532 auto filtered2 = ar.filter!"!(a.i & 1)"; 1533 1534 assert(serializeToJson(filtered1) == `[{"i":1},{"i":3},{"i":17}]`); 1535 assert(serializeToJson(filtered2) == `[{"i":4}]`); 1536 } 1537 1538 /// 1539 unittest 1540 { 1541 uint[2] ar = [1, 2]; 1542 assert(serializeToJson(ar) == `[1,2]`); 1543 assert(serializeToJson(ar[]) == `[1,2]`); 1544 assert(serializeToJson(ar[0 .. 0]) == `[]`); 1545 assert(serializeToJson((uint[]).init) == `null`); 1546 } 1547 1548 /// String-value associative array serialization 1549 void serializeValue(S, T)(ref S serializer, auto ref T[string] value) 1550 { 1551 if(value is null) 1552 { 1553 serializer.putValue(null); 1554 return; 1555 } 1556 auto state = serializer.structBegin(); 1557 foreach (key, ref val; value) 1558 { 1559 serializer.putKey(key); 1560 serializer.serializeValue(val); 1561 } 1562 serializer.structEnd(state); 1563 } 1564 1565 /// 1566 unittest 1567 { 1568 uint[string] ar = ["a" : 1]; 1569 assert(serializeToJson(ar) == `{"a":1}`); 1570 ar.remove("a"); 1571 assert(serializeToJson(ar) == `{}`); 1572 assert(serializeToJson((uint[string]).init) == `null`); 1573 } 1574 1575 /// Enumeration-value associative array serialization 1576 void serializeValue(S, V : const T[K], T, K)(ref S serializer, V value) 1577 if(is(K == enum)) 1578 { 1579 if(value is null) 1580 { 1581 serializer.putValue(null); 1582 return; 1583 } 1584 auto state = serializer.structBegin(); 1585 foreach (key, ref val; value) 1586 { 1587 serializer.putEscapedKey(key.to!string); 1588 serializer.putValue(val); 1589 } 1590 serializer.structEnd(state); 1591 } 1592 1593 /// 1594 unittest 1595 { 1596 enum E { a, b } 1597 uint[E] ar = [E.a : 1]; 1598 assert(serializeToJson(ar) == `{"a":1}`); 1599 ar.remove(E.a); 1600 assert(serializeToJson(ar) == `{}`); 1601 assert(serializeToJson((uint[string]).init) == `null`); 1602 } 1603 1604 /// integral typed value associative array serialization 1605 void serializeValue(S, V : const T[K], T, K)(ref S serializer, V value) 1606 if((isIntegral!K) && !is(K == enum)) 1607 { 1608 if(value is null) 1609 { 1610 serializer.putValue(null); 1611 return; 1612 } 1613 char[40] buffer = void; 1614 auto state = serializer.structBegin(); 1615 foreach (key, ref val; value) 1616 { 1617 import std.format : sformat; 1618 auto str = sformat(buffer[], "%d", key); 1619 serializer.putEscapedKey(str); 1620 .serializeValue(serializer, val); 1621 } 1622 serializer.structEnd(state); 1623 } 1624 1625 /// 1626 unittest 1627 { 1628 uint[short] ar = [256 : 1]; 1629 assert(serializeToJson(ar) == `{"256":1}`); 1630 ar.remove(256); 1631 assert(serializeToJson(ar) == `{}`); 1632 assert(serializeToJson((uint[string]).init) == `null`); 1633 assert(deserialize!(uint[short])(`{"256":1}`) == cast(uint[short]) [256 : 1]); 1634 } 1635 1636 /// Nullable type serialization 1637 void serializeValue(S, N)(ref S serializer, auto ref N value) 1638 if (isStdNullable!N && !isVariant!N) 1639 { 1640 if(value.isNull) 1641 { 1642 serializer.putValue(null); 1643 return; 1644 } 1645 serializer.serializeValue(value.get); 1646 } 1647 1648 /// 1649 unittest 1650 { 1651 import std.typecons; 1652 1653 struct Nested 1654 { 1655 float f; 1656 } 1657 1658 struct T 1659 { 1660 string str; 1661 Nullable!Nested nested; 1662 } 1663 1664 T t; 1665 assert(t.serializeToJson == `{"str":null,"nested":null}`); 1666 t.str = "txt"; 1667 t.nested = Nested(123); 1668 assert(t.serializeToJson == `{"str":"txt","nested":{"f":123.0}}`); 1669 } 1670 1671 /// Struct and class type serialization 1672 void serializeValue(S, V)(ref S serializer, auto ref V value) 1673 if((!isStdNullable!V || isVariant!V) && isAggregateType!V && !is(V : BigInt) && !isInputRange!V) 1674 { 1675 import mir.timestamp: Timestamp; 1676 import mir.algebraic : Algebraic; 1677 import mir.string_map : isStringMap; 1678 static if(is(V == class) || is(V == interface)) 1679 { 1680 if(value is null) 1681 { 1682 serializer.putValue(null); 1683 return; 1684 } 1685 } 1686 1687 static if (is(Unqual!V == Timestamp)) 1688 { 1689 serializer.putValue(value); 1690 } 1691 else 1692 static if (is(Unqual!V == Algebraic!TypeSet, TypeSet...)) 1693 { 1694 import mir.algebraic: visit; 1695 value.visit!((auto ref v) { 1696 alias T = typeof(v); 1697 static if (isStringMap!T ) 1698 { 1699 if(v == v.init) 1700 { 1701 auto valState = serializer.structBegin(); 1702 serializer.structEnd(valState); 1703 return; 1704 } 1705 } 1706 else 1707 static if (isAssociativeArray!T) 1708 { 1709 if(v is null) 1710 { 1711 auto valState = serializer.structBegin(); 1712 serializer.structEnd(valState); 1713 return; 1714 } 1715 } 1716 else 1717 static if (isSomeString!T) 1718 { 1719 if(v is null) 1720 { 1721 serializer.putValue(""); 1722 return; 1723 } 1724 } 1725 else 1726 static if (isDynamicArray!T) 1727 { 1728 if(v is null) 1729 { 1730 auto valState = serializer.listBegin(); 1731 serializer.listEnd(valState); 1732 return; 1733 } 1734 } 1735 .serializeValue(serializer, v); 1736 }); 1737 } 1738 else 1739 static if (isStringMap!V) 1740 { 1741 if(value == value.init) 1742 { 1743 serializer.putValue(null); 1744 return; 1745 } 1746 auto valState = serializer.structBegin(); 1747 foreach (i, key; value.keys) 1748 { 1749 serializer.putKey(key); 1750 serializer.serializeValue(value.values[i]); 1751 } 1752 serializer.structEnd(valState); 1753 return; 1754 } 1755 else 1756 static if(__traits(hasMember, V, "serialize")) 1757 { 1758 value.serialize(serializer); 1759 } 1760 else 1761 static if (hasUDA!(V, serdeProxy)) 1762 { 1763 serializer.serializeValue(value.to!(serdeGetProxy!V)); 1764 } 1765 else 1766 { 1767 auto state = serializer.structBegin(); 1768 foreach(member; aliasSeqOf!(SerializableMembers!V)) 1769 {{ 1770 enum key = serdeGetKeyOut!(__traits(getMember, value, member)); 1771 1772 static if (key !is null) 1773 { 1774 static if (hasUDA!(__traits(getMember, value, member), serdeIgnoreDefault)) 1775 { 1776 if (__traits(getMember, value, member) == __traits(getMember, V.init, member)) 1777 continue; 1778 } 1779 1780 static if(hasUDA!(__traits(getMember, value, member), serdeIgnoreOutIf)) 1781 { 1782 alias pred = serdeGetIgnoreOutIf!(__traits(getMember, value, member)); 1783 if (pred(__traits(getMember, value, member))) 1784 continue; 1785 } 1786 static if(hasUDA!(__traits(getMember, value, member), serdeTransformOut)) 1787 { 1788 alias f = serdeGetTransformOut!(__traits(getMember, value, member)); 1789 auto val = f(__traits(getMember, value, member)); 1790 } 1791 else 1792 { 1793 auto val = __traits(getMember, value, member); 1794 } 1795 1796 serializer.putEscapedKey(key); 1797 1798 static if(hasUDA!(__traits(getMember, value, member), serdeLikeList)) 1799 { 1800 alias V = typeof(val); 1801 static if(is(V == interface) || is(V == class) || is(V : E[], E)) 1802 { 1803 if(val is null) 1804 { 1805 serializer.putValue(null); 1806 continue; 1807 } 1808 } 1809 auto valState = serializer.listBegin(); 1810 foreach (ref elem; val) 1811 { 1812 serializer.elemBegin; 1813 serializer.serializeValue(elem); 1814 } 1815 serializer.listEnd(valState); 1816 } 1817 else 1818 static if(hasUDA!(__traits(getMember, value, member), serdeLikeStruct)) 1819 { 1820 static if(is(V == interface) || is(V == class) || is(V : E[T], E, T)) 1821 { 1822 if(val is null) 1823 { 1824 serializer.putValue(null); 1825 continue F; 1826 } 1827 } 1828 auto valState = serializer.structBegin(); 1829 foreach (key, elem; val) 1830 { 1831 serializer.putKey(key); 1832 serializer.serializeValue(elem); 1833 } 1834 serializer.structEnd(valState); 1835 } 1836 else 1837 static if(hasUDA!(__traits(getMember, value, member), serdeProxy)) 1838 { 1839 serializer.serializeValue(val.to!(serdeGetProxy!(__traits(getMember, value, member)))); 1840 } 1841 else 1842 { 1843 serializer.serializeValue(val); 1844 } 1845 } 1846 }} 1847 static if(__traits(hasMember, V, "finalizeSerialization")) 1848 { 1849 value.finalizeSerialization(serializer); 1850 } 1851 serializer.structEnd(state); 1852 } 1853 } 1854 1855 /// Alias this support 1856 unittest 1857 { 1858 struct S 1859 { 1860 int u; 1861 } 1862 1863 struct C 1864 { 1865 int b; 1866 S s; 1867 alias s this; 1868 } 1869 1870 assert(C(4, S(3)).serializeToJson == `{"u":3,"b":4}`); 1871 } 1872 1873 /// Custom `serialize` 1874 unittest 1875 { 1876 import mir.conv: to; 1877 1878 struct S 1879 { 1880 void serialize(S)(ref S serializer) const 1881 { 1882 auto state = serializer.structBegin; 1883 serializer.putEscapedKey("foo"); 1884 serializer.putValue("bar"); 1885 serializer.structEnd(state); 1886 } 1887 } 1888 enum json = `{"foo":"bar"}`; 1889 assert(serializeToJson(S()) == json); 1890 assert(serializeToFghj(S()).to!string == json); 1891 } 1892 1893 /// $(GMREF mir-core, mir, algebraic) support. 1894 unittest 1895 { 1896 import mir.algebraic: Variant, Nullable, This; 1897 alias V = Nullable!(double, string, This[], This[string]); 1898 V v; 1899 assert(v.serializeToJson == "null", v.serializeToJson); 1900 v = [V(2), V("str"), V(["key":V(1.0)])]; 1901 assert(v.serializeToJson == `[2.0,"str",{"key":1.0}]`); 1902 } 1903 1904 /// $(GMREF mir-core, mir, algebraic) with manual serialization. 1905 unittest 1906 { 1907 import fghj.fghj; 1908 1909 static struct Response 1910 { 1911 import mir.algebraic: TaggedVariant; 1912 1913 alias Union = TaggedVariant!( 1914 ["double_", "string", "array", "table"], 1915 double, 1916 string, 1917 Response[], 1918 Response[string], 1919 ); 1920 1921 Union data; 1922 alias Tag = Union.Kind; 1923 // propogates opEquals, opAssign, and other primitives 1924 alias data this; 1925 1926 static foreach (T; Union.AllowedTypes) 1927 this(T v) @safe pure nothrow @nogc { data = v; } 1928 1929 void serialize(S)(ref S serializer) const 1930 { 1931 import fghj: serializeValue; 1932 import mir.algebraic: visit; 1933 1934 auto o = serializer.structBegin(); 1935 serializer.putKey("tag"); 1936 serializer.serializeValue(kind); 1937 serializer.putKey("data"); 1938 data.visit!( 1939 (double v) => serializer.serializeValue(v), // specialization for double if required 1940 (const Response[string] v) => serializer.serializeValue(cast(const(Response)[string])v), 1941 (v) => serializer.serializeValue(v), 1942 ); 1943 serializer.structEnd(o); 1944 } 1945 1946 SerdeException deserializeFromFghj(Fghj fghjData) 1947 { 1948 import fghj : deserializeValue; 1949 import std.traits : EnumMembers; 1950 1951 Tag tag; 1952 if (auto e = fghjData["tag"].deserializeValue(tag)) 1953 return e; 1954 final switch (tag) 1955 { 1956 foreach (m; EnumMembers!Tag) 1957 { 1958 case m: { 1959 alias T = Union.AllowedTypes[m]; 1960 data = T.init; 1961 if (auto e = fghjData["data"].deserializeValue(data.trustedGet!T)) 1962 return e; 1963 break; 1964 } 1965 } 1966 } 1967 return null; 1968 } 1969 } 1970 1971 Response v = 3.0; 1972 assert(v.kind == Response.Tag.double_); 1973 v = "str"; 1974 assert(v == "str"); 1975 1976 import fghj; 1977 assert(v.serializeToJson == `{"tag":"string","data":"str"}`); 1978 v = Response.init; 1979 v = `{"tag":"array","data":[{"tag":"string","data":"S"}]}`.deserialize!Response; 1980 assert(v.kind == Response.Tag.array); 1981 assert(v.get!(Response[])[0] == "S"); 1982 } 1983 1984 /// Deserialize `null` value 1985 SerdeException deserializeValue(T : typeof(null))(Fghj data, T) 1986 { 1987 auto kind = data.kind; 1988 if(kind != Fghj.Kind.null_) 1989 return unexpectedKind(kind); 1990 return null; 1991 } 1992 1993 /// 1994 unittest 1995 { 1996 assert(deserializeValue(serializeToFghj(null), null) is null); 1997 } 1998 1999 /// Deserialize boolean value 2000 SerdeException deserializeValue(T : bool)(Fghj data, ref T value) pure @safe 2001 { 2002 auto kind = data.kind; 2003 with(Fghj.Kind) switch(kind) 2004 { 2005 case false_: 2006 value = false; 2007 return null; 2008 case true_: 2009 value = true; 2010 return null; 2011 default: 2012 return unexpectedKind(kind); 2013 } 2014 } 2015 2016 /// 2017 pure unittest 2018 { 2019 assert(deserialize!bool(serializeToFghj(true))); 2020 assert(deserialize!bool(serializeToJson(true))); 2021 } 2022 2023 /++ 2024 Deserialize numeric value. 2025 2026 Special_deserialisation_string_values: 2027 2028 $(TABLE 2029 $(TR $(TD `"+NAN"`)) 2030 $(TR $(TD `"+NaN"`)) 2031 $(TR $(TD `"+nan"`)) 2032 $(TR $(TD `"-NAN"`)) 2033 $(TR $(TD `"-NaN"`)) 2034 $(TR $(TD `"-nan"`)) 2035 $(TR $(TD `"NAN"`)) 2036 $(TR $(TD `"NaN"`)) 2037 $(TR $(TD `"nan"`)) 2038 $(TR $(TD `"+INF"`)) 2039 $(TR $(TD `"+Inf"`)) 2040 $(TR $(TD `"+inf"`)) 2041 $(TR $(TD `"-INF"`)) 2042 $(TR $(TD `"-Inf"`)) 2043 $(TR $(TD `"-inf"`)) 2044 $(TR $(TD `"INF"`)) 2045 $(TR $(TD `"Inf"`)) 2046 $(TR $(TD `"inf"`)) 2047 ) 2048 2049 +/ 2050 SerdeException deserializeValue(V)(Fghj data, ref V value) 2051 if((isNumeric!V && !is(V == enum))) 2052 { 2053 auto kind = data.kind; 2054 2055 static if (isFloatingPoint!V) 2056 { 2057 if (kind == Fghj.Kind.null_) 2058 { 2059 value = V.nan; 2060 return null; 2061 } 2062 if (kind == Fghj.Kind..string) 2063 { 2064 const(char)[] v; 2065 .deserializeScopedString(data, v); 2066 switch (v) 2067 { 2068 case "+NAN": 2069 case "+NaN": 2070 case "+nan": 2071 case "-NAN": 2072 case "-NaN": 2073 case "-nan": 2074 case "NAN": 2075 case "NaN": 2076 case "nan": 2077 value = V.nan; 2078 return null; 2079 case "+INF": 2080 case "+Inf": 2081 case "+inf": 2082 case "INF": 2083 case "Inf": 2084 case "inf": 2085 value = V.infinity; 2086 return null; 2087 case "-INF": 2088 case "-Inf": 2089 case "-inf": 2090 value = -V.infinity; 2091 return null; 2092 default: 2093 import mir.conv : to; 2094 value = data.to!V; 2095 return null; 2096 } 2097 } 2098 } 2099 2100 if(kind != Fghj.Kind.number) 2101 return unexpectedKind(kind); 2102 value = (cast(string) data.data[2 .. $]).to!V; 2103 return null; 2104 } 2105 2106 /// 2107 unittest 2108 { 2109 import std.bigint; 2110 2111 assert(deserialize!ulong (serializeToFghj(20)) == ulong (20)); 2112 assert(deserialize!ulong (serializeToJson(20)) == ulong (20)); 2113 assert(deserialize!double(serializeToFghj(20)) == double(20)); 2114 assert(deserialize!double(serializeToJson(20)) == double(20)); 2115 assert(deserialize!BigInt(serializeToFghj(20)) == BigInt(20)); 2116 assert(deserialize!BigInt(serializeToJson(20)) == BigInt(20)); 2117 2118 assert(deserialize!float (serializeToJson ("2.40")) == float (2.40)); 2119 assert(deserialize!double(serializeToJson ("2.40")) == double(2.40)); 2120 assert(deserialize!double(serializeToFghj("-2.40")) == double(-2.40)); 2121 2122 import std.math : isNaN, isInfinity; 2123 assert(deserialize!float (serializeToJson ("+NaN")).isNaN); 2124 assert(deserialize!float (serializeToJson ("INF")).isInfinity); 2125 assert(deserialize!float (serializeToJson ("-inf")).isInfinity); 2126 } 2127 2128 /// Deserialize enum value 2129 SerdeException deserializeValue(V)(Fghj data, ref V value) 2130 if(is(V == enum)) 2131 { 2132 static if (hasUDA!(V, serdeProxy)) 2133 { 2134 serdeGetProxy!V proxy; 2135 enum S = hasUDA!(value, serdeScoped) && __traits(compiles, .deserializeScopedString(data, proxy)); 2136 alias Fun = Select!(S, .deserializeScopedString, .deserializeValue); 2137 Fun(data, proxy); 2138 value = proxy.to!V; 2139 } 2140 else 2141 { 2142 string s; 2143 data.deserializeScopedString(s); 2144 import mir.ndslice.fuse: fuse; 2145 import mir.array.allocation: array; 2146 import mir.ndslice.topology: map; 2147 static immutable allowedKeys = [EnumMembers!V].map!serdeGetKeysIn.array; 2148 if (!serdeParseEnum(s, value)) 2149 throw new Exception("Unable to deserialize string '" ~ s ~ "' to " ~ V.stringof ~ "Allowed keys:" ~ allowedKeys.stringof); 2150 } 2151 return null; 2152 } 2153 2154 /// 2155 unittest 2156 { 2157 @serdeIgnoreCase enum Key { foo } 2158 assert(deserialize!Key(`"FOO"`) == Key.foo); 2159 assert(deserialize!Key(serializeToFghj("foo")) == Key.foo); 2160 } 2161 2162 /++ 2163 Deserializes scoped string value. 2164 This function does not allocate a new string and just make a raw cast of FGHJ data. 2165 +/ 2166 SerdeException deserializeScopedString(V : const(char)[])(Fghj data, ref V value) 2167 { 2168 auto kind = data.kind; 2169 with(Fghj.Kind) switch(kind) 2170 { 2171 case string: 2172 value = cast(V) data.data[5 .. $]; 2173 return null; 2174 case null_: 2175 value = null; 2176 return null; 2177 default: 2178 return unexpectedKind(kind); 2179 } 2180 } 2181 2182 /++ 2183 Deserializes string value. 2184 This function allocates new string. 2185 +/ 2186 SerdeException deserializeValue(V)(Fghj data, ref V value) 2187 if(is(V : const(char)[]) && !isAggregateType!V && !is(V == enum) && !isStdNullable!V) 2188 { 2189 auto kind = data.kind; 2190 with(Fghj.Kind) switch(kind) 2191 { 2192 case string: 2193 value = (() @trusted => cast(V) (data.data[5 .. $]).dup)(); 2194 return null; 2195 case null_: 2196 value = null; 2197 return null; 2198 default: 2199 return unexpectedKind(kind); 2200 } 2201 } 2202 2203 // issue #94/#95/#97 2204 /// String enums supports only enum keys 2205 unittest 2206 { 2207 enum SimpleEnum : string 2208 { 2209 @serdeKeys("se1", "se1value") 2210 se1 = "se1value", 2211 2212 @serdeKeys("se2", "se2value") 2213 se2 = "se2value", 2214 2215 @serdeKeys("se3", "se3value") 2216 se3 = "se3value", 2217 } 2218 2219 struct Simple 2220 { 2221 SimpleEnum en; 2222 SimpleEnum ex; 2223 } 2224 2225 Simple simple = `{"en":"se2", "ex":"se3value"}`.deserialize!Simple; 2226 assert(simple.en == SimpleEnum.se2); 2227 assert(simple.ex == SimpleEnum.se3); 2228 } 2229 2230 /// issue #115 2231 unittest 2232 { 2233 import fghj; 2234 import std.typecons; 2235 2236 struct Example 2237 { 2238 @serdeOptional 2239 Nullable!string field1; 2240 } 2241 2242 assert(`{}`.deserialize!Example == Example()); 2243 assert(Example().serializeToJson == `{"field1":null}`); 2244 } 2245 2246 /// 2247 unittest 2248 { 2249 assert(deserialize!string(serializeToJson(null)) is null); 2250 assert(deserialize!string(serializeToFghj(null)) is null); 2251 assert(deserialize!string(serializeToJson("\tbar")) == "\tbar"); 2252 assert(deserialize!string(serializeToFghj("\"bar")) == "\"bar"); 2253 } 2254 2255 /// Deserialize single char 2256 SerdeException deserializeValue(V)(Fghj data, ref V value) 2257 if (is(V == char) && !is(V == enum)) 2258 { 2259 return deserializeValue(data, *(()@trusted=> cast(char[1]*)&value)()); 2260 } 2261 2262 /// 2263 unittest 2264 { 2265 assert(deserialize!char(`"a"`) == 'a'); 2266 assert(deserialize!byte(`-4`) == -4); // regression control 2267 } 2268 2269 /// Deserialize array 2270 SerdeException deserializeValue(V : T[], T)(Fghj data, ref V value) 2271 if(!isSomeChar!T && !isStaticArray!V) 2272 { 2273 const kind = data.kind; 2274 with(Fghj.Kind) switch(kind) 2275 { 2276 case array: 2277 import std.algorithm.searching: count; 2278 auto elems = data.byElement; 2279 // create array of properly initialized (by means of ctor) elements 2280 static if (__traits(compiles, {value = new T[100];})) 2281 { 2282 value = new T[elems.save.count]; 2283 foreach(ref e; value) 2284 { 2285 static if(is(T == class)) e = new T; 2286 if (auto exc = .deserializeValue(elems.front, e)) 2287 return exc; 2288 elems.popFront; 2289 } 2290 } 2291 else 2292 static assert(0, "Type `" ~ T.stringof ~ "` should have default value!"); 2293 assert(elems.empty); 2294 return null; 2295 case null_: 2296 value = null; 2297 return null; 2298 default: 2299 return unexpectedKind(kind); 2300 } 2301 } 2302 2303 /// 2304 unittest 2305 { 2306 assert(deserialize!(int[])(serializeToJson(null)) is null); 2307 assert(deserialize!(int[])(serializeToFghj(null)) is null); 2308 assert(deserialize!(int[])(serializeToJson([1, 3, 4])) == [1, 3, 4]); 2309 assert(deserialize!(int[])(serializeToFghj([1, 3, 4])) == [1, 3, 4]); 2310 } 2311 2312 /// Deserialize static array 2313 SerdeException deserializeValue(V : T[N], T, size_t N)(Fghj data, ref V value) 2314 { 2315 auto kind = data.kind; 2316 with(Fghj.Kind) switch(kind) 2317 { 2318 static if(is(Unqual!T == char)) 2319 { 2320 case string: 2321 auto str = cast(immutable(char)[]) data; 2322 // if source is shorter than destination fill the rest by zeros 2323 // if source is longer copy only needed part of it 2324 if (str.length > value.length) 2325 str = str[0..value.length]; 2326 else 2327 value[] = '\0'; 2328 2329 import std.algorithm : copy; 2330 copy(str, value[]); 2331 return null; 2332 } 2333 case array: 2334 auto elems = data.byElement; 2335 foreach(ref e; value) 2336 { 2337 if(elems.empty) 2338 return null; 2339 if (auto exc = .deserializeValue(elems.front, e)) 2340 return exc; 2341 elems.popFront; 2342 } 2343 return null; 2344 case null_: 2345 return null; 2346 default: 2347 return unexpectedKind!("Failed to deserialize value of " ~ V.stringof)(kind); 2348 } 2349 } 2350 2351 /// 2352 unittest 2353 { 2354 assert(deserialize!(int[4])(serializeToJson(null)) == [0, 0, 0, 0]); 2355 assert(deserialize!(int[4])(serializeToFghj(null)) == [0, 0, 0, 0]); 2356 assert(deserialize!(int[4])(serializeToJson([1, 3, 4])) == [1, 3, 4, 0]); 2357 assert(deserialize!(int[4])(serializeToFghj([1, 3, 4])) == [1, 3, 4, 0]); 2358 assert(deserialize!(int[2])(serializeToJson([1, 3, 4])) == [1, 3]); 2359 assert(deserialize!(int[2])(serializeToFghj([1, 3, 4])) == [1, 3]); 2360 2361 assert(deserialize!(char[2])(serializeToFghj(['a','b'])) == ['a','b']); 2362 assert(deserialize!(char[2])(serializeToFghj(['a','\0'])) == ['a','\0']); 2363 assert(deserialize!(char[2])(serializeToFghj(['a','\255'])) == ['a','\255']); 2364 assert(deserialize!(char[2])(serializeToFghj(['\255'])) == ['\255','\0']); 2365 assert(deserialize!(char[2])(serializeToFghj(['\255', '\255', '\255'])) == ['\255','\255']); 2366 } 2367 2368 /// AA with value of aggregate type 2369 unittest 2370 { 2371 struct Foo 2372 { 2373 2374 } 2375 2376 assert (deserialize!(Foo[int])(serializeToJson([1: Foo()])) == [1:Foo()]); 2377 } 2378 2379 /// Deserialize string-value associative array 2380 SerdeException deserializeValue(V : T[string], T)(Fghj data, ref V value) 2381 { 2382 auto kind = data.kind; 2383 with(Fghj.Kind) switch(kind) 2384 { 2385 case object: 2386 foreach(elem; data.byKeyValue) 2387 { 2388 T v; 2389 if (auto exc = .deserializeValue(elem.value, v)) 2390 return exc; 2391 value[elem.key.idup] = v; 2392 } 2393 return null; 2394 case null_: 2395 value = null; 2396 return null; 2397 default: 2398 return unexpectedKind(kind); 2399 } 2400 } 2401 2402 /// 2403 unittest 2404 { 2405 assert(deserialize!(int[string])(serializeToJson(null)) is null); 2406 assert(deserialize!(int[string])(serializeToFghj(null)) is null); 2407 assert(deserialize!(int[string])(serializeToJson(["a" : 1, "b" : 2])) == ["a" : 1, "b" : 2]); 2408 assert(deserialize!(int[string])(serializeToFghj(["a" : 1, "b" : 2])) == ["a" : 1, "b" : 2]); 2409 } 2410 2411 unittest 2412 { 2413 int[string] r = ["a" : 1]; 2414 serializeToFghj(null).deserializeValue(r); 2415 assert(r is null); 2416 } 2417 2418 /// Deserialize enumeration-value associative array 2419 SerdeException deserializeValue(V : T[E], T, E)(Fghj data, ref V value) 2420 if(is(E == enum)) 2421 { 2422 auto kind = data.kind; 2423 with(Fghj.Kind) switch(kind) 2424 { 2425 case object: 2426 foreach(elem; data.byKeyValue) 2427 { 2428 T v; 2429 if (auto exc = .deserializeValue(elem.value, v)) 2430 return exc; 2431 value[elem.key.to!E] = v; 2432 } 2433 return null; 2434 case null_: 2435 value = null; 2436 return null; 2437 default: 2438 return unexpectedKind(kind); 2439 } 2440 } 2441 2442 /// 2443 unittest 2444 { 2445 enum E {a, b} 2446 assert(deserialize!(int[E])(serializeToJson(null)) is null); 2447 assert(deserialize!(int[E])(serializeToFghj(null)) is null); 2448 assert(deserialize!(int[E])(serializeToJson([E.a : 1, E.b : 2])) == [E.a : 1, E.b : 2]); 2449 assert(deserialize!(int[E])(serializeToFghj([E.a : 1, E.b : 2])) == [E.a : 1, E.b : 2]); 2450 } 2451 2452 unittest 2453 { 2454 enum E {a, b} 2455 int[E] r = [E.a : 1]; 2456 serializeToFghj(null).deserializeValue(r); 2457 assert(r is null); 2458 } 2459 2460 /// Deserialize associative array with integral type key 2461 SerdeException deserializeValue(V : T[K], T, K)(Fghj data, ref V value) 2462 if((isIntegral!K) && !is(K == enum)) 2463 { 2464 auto kind = data.kind; 2465 with(Fghj.Kind) switch(kind) 2466 { 2467 case object: 2468 foreach(elem; data.byKeyValue) 2469 { 2470 T v; 2471 if (auto exc = .deserializeValue(elem.value, v)) 2472 return exc; 2473 value[elem.key.to!K] = v; 2474 } 2475 return null; 2476 case null_: 2477 value = null; 2478 return null; 2479 default: 2480 return unexpectedKind(kind); 2481 } 2482 } 2483 2484 /// 2485 unittest 2486 { 2487 assert(deserialize!(int[int])(serializeToJson(null)) is null); 2488 assert(deserialize!(int[int])(serializeToFghj(null)) is null); 2489 assert(deserialize!(int[int])(serializeToJson([2 : 1, 40 : 2])) == [2 : 1, 40 : 2]); 2490 assert(deserialize!(int[int])(serializeToFghj([2 : 1, 40 : 2])) == [2 : 1, 40 : 2]); 2491 } 2492 2493 unittest 2494 { 2495 int[int] r = [3 : 1]; 2496 serializeToFghj(null).deserializeValue(r); 2497 assert(r is null); 2498 } 2499 2500 /// 2501 unittest 2502 { 2503 import std.typecons; 2504 2505 struct Nested 2506 { 2507 float f; 2508 } 2509 2510 struct T 2511 { 2512 string str; 2513 Nullable!Nested nested; 2514 @serdeOptional 2515 Nullable!bool nval; 2516 } 2517 2518 T t; 2519 assert(deserialize!T(`{"str":null,"nested":null}`) == t); 2520 t.str = "txt"; 2521 t.nested = Nested(123); 2522 t.nval = false; 2523 assert(deserialize!T(`{"str":"txt","nested":{"f":123},"nval":false}`) == t); 2524 } 2525 2526 struct Impl 2527 { 2528 @safe pure @nogc static: 2529 2530 enum customDeserializeValueMehtodName = "deserializeFromFghj"; 2531 2532 bool isAnyNull(Fghj data) 2533 { 2534 return data.kind == Fghj.Kind.null_; 2535 } 2536 2537 bool isObjectNull(Fghj data) 2538 { 2539 return data.kind == Fghj.Kind.null_; 2540 } 2541 2542 bool isObject(Fghj data) 2543 { 2544 return data.kind == Fghj.Kind.object; 2545 } 2546 2547 SerdeException unexpectedData(string msg)(Fghj data) 2548 { 2549 return unexpectedKind(data.kind); 2550 } 2551 } 2552 2553 /// Deserialize aggregate value 2554 SerdeException deserializeValue(V)(Fghj data, ref V value) 2555 if(isAggregateType!V) 2556 { 2557 import mir.algebraic; 2558 import mir.string_map; 2559 import mir.timestamp; 2560 static if (is(V == Timestamp)) 2561 { 2562 const(char)[] str; 2563 if (auto exc = deserializeValue(data, str)) 2564 return exc; 2565 value = Timestamp(str); 2566 return null; 2567 } 2568 else 2569 static if (is(V == StringMap!T, T)) 2570 { 2571 auto kind = data.kind; 2572 with(Fghj.Kind) switch(kind) 2573 { 2574 case object: 2575 foreach(elem; data.byKeyValue) 2576 { 2577 T v; 2578 if (auto exc = .deserializeValue(elem.value, v)) 2579 return exc; 2580 value[elem.key.idup] = v; 2581 } 2582 return null; 2583 case null_: 2584 value = null; 2585 return null; 2586 default: 2587 return unexpectedKind(kind); 2588 } 2589 } 2590 else 2591 static if (is(V == Algebraic!TypeSet, TypeSet...)) 2592 { 2593 import std.meta: anySatisfy, Filter; 2594 import mir.internal.meta: Contains; 2595 alias Types = V.AllowedTypes; 2596 alias contains = Contains!Types; 2597 import mir.algebraic: isNullable; 2598 static if (isNullable!V && TypeSet.length == 2) 2599 { 2600 if (data.kind == Fghj.Kind.null_) 2601 { 2602 value = null; 2603 return null; 2604 } 2605 2606 V.AllowedTypes[1] payload; 2607 if (auto exc = .deserializeValue(data, payload)) 2608 return exc; 2609 value = payload; 2610 return null; 2611 } 2612 else 2613 switch (data.kind) 2614 { 2615 static if (contains!(typeof(null))) 2616 { 2617 case Fghj.Kind.null_: 2618 { 2619 value = null; 2620 return null; 2621 } 2622 } 2623 2624 static if (contains!bool) 2625 { 2626 case Fghj.Kind.true_: 2627 { 2628 value = true; 2629 return null; 2630 } 2631 case Fghj.Kind.false_: 2632 { 2633 value = false; 2634 return null; 2635 } 2636 } 2637 2638 static if (contains!string) 2639 { 2640 case Fghj.Kind..string: 2641 { 2642 string str; 2643 if (auto exc = deserializeValue(data, str)) 2644 return exc; 2645 value = str; 2646 return null; 2647 } 2648 } 2649 2650 static if (contains!long || contains!double) 2651 { 2652 case Fghj.Kind.number: 2653 { 2654 import mir.bignum.decimal; 2655 DecimalExponentKey key; 2656 Decimal!256 decimal = void; 2657 auto str = (()@trusted => cast(string) data.data[2 .. $])(); 2658 2659 enum bool allowSpecialValues = false; 2660 enum bool allowDotOnBounds = false; 2661 enum bool allowDExponent = false; 2662 enum bool allowStartingPlus = false; 2663 enum bool allowUnderscores = false; 2664 enum bool allowLeadingZeros = false; 2665 enum bool allowExponent = true; 2666 enum bool checkEmpty = false; 2667 2668 if (!decimal.fromStringImpl!( 2669 char, 2670 allowSpecialValues, 2671 allowDotOnBounds, 2672 allowDExponent, 2673 allowStartingPlus, 2674 allowUnderscores, 2675 allowLeadingZeros, 2676 allowExponent, 2677 checkEmpty, 2678 )(str, key)) 2679 return new SerdeException("Fghj: can't parse number string: " ~ str); 2680 2681 if (key || !contains!long) 2682 { 2683 static if (contains!double) 2684 { 2685 value = cast(double) decimal; 2686 return null; 2687 } 2688 else 2689 { 2690 return new SerdeException("Fghj: can't parse integer string: " ~ str); 2691 } 2692 } 2693 static if (contains!long) 2694 { 2695 auto bigintView = decimal.coefficient.view; 2696 auto ret = cast(long) bigintView; 2697 if (ret != bigintView) { 2698 return new SerdeException("Fghj: integer overflow"); 2699 } 2700 value = ret; 2701 } 2702 return null; 2703 } 2704 } 2705 2706 static if (anySatisfy!(templateAnd!(isArray, templateNot!isSomeString), Types)) 2707 { 2708 case Fghj.Kind.array: 2709 { 2710 alias ArrayTypes = Filter!(templateAnd!(isArray, templateNot!isSomeString), Types); 2711 static assert(ArrayTypes.length == 1, ArrayTypes.stringof); 2712 ArrayTypes[0] array; 2713 if (auto exc = deserializeValue(data, array)) 2714 return exc; 2715 value = array; 2716 return null; 2717 } 2718 } 2719 2720 static if (anySatisfy!(isStringMap, Types)) 2721 { 2722 case Fghj.Kind.object: 2723 { 2724 alias MapTypes = Filter!(isStringMap, Types); 2725 static assert(MapTypes.length == 1, MapTypes.stringof); 2726 MapTypes[0] object; 2727 if (auto exc = deserializeValue(data, object)) 2728 return exc; 2729 value = object; 2730 return null; 2731 } 2732 } 2733 else 2734 static if (anySatisfy!(isAssociativeArray, Types)) 2735 { 2736 case Fghj.Kind.object: 2737 { 2738 alias AATypes = Filter!(isAssociativeArray, Types); 2739 static assert(AATypes.length == 1, AATypes.stringof); 2740 AATypes[0] object; 2741 if (auto exc = deserializeValue(data, object)) 2742 return exc; 2743 value = object; 2744 return null; 2745 } 2746 } 2747 2748 default: 2749 return unexpectedKind(data.kind); 2750 } 2751 } 2752 else 2753 static if (is(V == BigInt)) 2754 { 2755 if (data.kind != Fghj.Kind.number) 2756 return unexpectedKind(data.kind); 2757 value = BigInt((()@trusted => cast(string) data.data[2 .. $])()); 2758 return null; 2759 } 2760 else 2761 static if (isStdNullable!V) 2762 { 2763 if (data.kind == Fghj.Kind.null_) 2764 { 2765 value.nullify; 2766 return null; 2767 } 2768 2769 typeof(value.get) payload; 2770 if (auto exc = .deserializeValue(data, payload)) 2771 return exc; 2772 value = payload; 2773 return null; 2774 } 2775 else 2776 static if (__traits(hasMember, value, "deserializeFromFghj")) 2777 { 2778 return __traits(getMember, value, "deserializeFromFghj")(data); 2779 } 2780 else 2781 static if (hasUDA!(V, serdeProxy)) 2782 {{ 2783 serdeGetProxy!V proxy; 2784 enum S = hasUDA!(value, serdeScoped) && __traits(compiles, .deserializeScopedString(data, proxy)); 2785 alias Fun = Select!(S, .deserializeScopedString, .deserializeValue); 2786 if (auto exc = Fun(data, proxy)) 2787 return exc; 2788 value = proxy.to!V; 2789 return null; 2790 }} 2791 else 2792 { 2793 if (!(data.kind == Fghj.Kind.object)) 2794 { 2795 static if(__traits(compiles, value = null)) 2796 { 2797 if (data.kind == Fghj.Kind.null_) 2798 { 2799 value = null; 2800 return null; 2801 } 2802 } 2803 return unexpectedKind!("Cann't deserialize " ~ V.stringof ~ ". Unexpected data:")(data.kind); 2804 } 2805 2806 static if(is(V == class) || is(V == interface)) 2807 { 2808 if(value is null) 2809 { 2810 static if(__traits(compiles, value = new V)) 2811 { 2812 value = new V; 2813 } 2814 else 2815 { 2816 return unexpectedKind(data.kind, "Object / interface must be either not null or have a a default constructor."); 2817 } 2818 } 2819 } 2820 2821 SerdeFlags!V requiredFlags; 2822 2823 static if (hasUDA!(V, serdeOrderedIn)) 2824 { 2825 SerdeOrderedDummy!V temporal; 2826 if (auto exc = .deserializeValue(data, temporal)) 2827 return exc; 2828 temporal.serdeFinalizeTarget(value, requiredFlags); 2829 } 2830 else 2831 { 2832 import std.meta: aliasSeqOf; 2833 2834 alias impl = deserializeValueMemberImpl!(deserializeValue, deserializeScopedString); 2835 2836 static immutable exc(string member) = new SerdeException("FGHJ deserialisation: non-optional member '" ~ member ~ "' in " ~ V.stringof ~ " is missing."); 2837 2838 static if (hasUDA!(V, serdeRealOrderedIn)) 2839 { 2840 static foreach(member; serdeFinalProxyDeserializableMembers!V) 2841 {{ 2842 enum keys = serdeGetKeysIn!(__traits(getMember, value, member)); 2843 static if (keys.length) 2844 { 2845 foreach(elem; data.byKeyValue) 2846 { 2847 switch(elem.key) 2848 { 2849 static foreach (key; keys) 2850 { 2851 case key: 2852 } 2853 if (auto mexp = impl!member(elem.value, value, requiredFlags)) 2854 return mexp; 2855 break; 2856 default: 2857 } 2858 } 2859 } 2860 2861 static if (!hasUDA!(__traits(getMember, value, member), serdeOptional)) 2862 if (!__traits(getMember, requiredFlags, member)) 2863 return exc!member; 2864 }} 2865 } 2866 else 2867 { 2868 foreach(elem; data.byKeyValue) 2869 { 2870 S: switch(elem.key) 2871 { 2872 static foreach(member; serdeFinalProxyDeserializableMembers!V) 2873 {{ 2874 enum keys = serdeGetKeysIn!(__traits(getMember, value, member)); 2875 static if (keys.length) 2876 { 2877 static foreach (key; keys) 2878 { 2879 case key: 2880 } 2881 if (auto mexp = impl!member(elem.value, value, requiredFlags)) 2882 return mexp; 2883 break S; 2884 } 2885 }} 2886 default: 2887 } 2888 } 2889 2890 static foreach(member; __traits(allMembers, SerdeFlags!V)) 2891 static if (!hasUDA!(__traits(getMember, value, member), serdeOptional)) 2892 if (!__traits(getMember, requiredFlags, member)) 2893 return exc!member; 2894 } 2895 } 2896 2897 static if(__traits(hasMember, V, "finalizeDeserialization")) 2898 { 2899 value.finalizeDeserialization(data); 2900 } 2901 static if(__traits(hasMember, V, "serdeFinalizeWithFlags")) 2902 { 2903 value.serdeFinalizeWithFlags(requiredFlags); 2904 } 2905 static if(__traits(hasMember, V, "serdeFinalize")) 2906 { 2907 value.serdeFinalize(); 2908 } 2909 return null; 2910 } 2911 } 2912 2913 /// StringMap support 2914 unittest 2915 { 2916 import mir.string_map; 2917 auto map = `{"b" : 1.0, "a" : 2}`.deserialize!(StringMap!double); 2918 assert(map.keys == ["b", "a"]); 2919 assert(map.values == [1.0, 2.0]); 2920 assert(map.serializeToJson == `{"b":1.0,"a":2.0}`); 2921 2922 } 2923 2924 /// JsonAlgebraic alias support 2925 unittest 2926 { 2927 import mir.algebraic_alias.json; 2928 auto value = `{"b" : 1.0, "a" : [1, true, false, null, "str"]}`.deserialize!JsonAlgebraic; 2929 assert(value.kind == JsonAlgebraic.Kind.object); 2930 2931 auto object = value.get!(StringMap!JsonAlgebraic); 2932 assert(object.keys == ["b", "a"]); // sequental order 2933 assert(object["b"].get!double == 1.0); 2934 object["b"].get!double += 4; 2935 2936 auto array = object["a"].get!(JsonAlgebraic[]); 2937 assert(array[0].get!long == 1); 2938 array[0].get!long += 10; 2939 assert(array[1].get!bool == true); 2940 assert(array[2].get!bool == false); 2941 assert(array[3].isNull); 2942 assert(array[3].get!(typeof(null)) is null); 2943 assert(array[4].get!string == "str"); 2944 2945 assert(value.serializeToJson == `{"b":5.0,"a":[11,true,false,null,"str"]}`); 2946 value = [JsonAlgebraic[].init.JsonAlgebraic, StringMap!JsonAlgebraic.init.JsonAlgebraic, string.init.JsonAlgebraic]; 2947 // algebraics have type safe serialization instead of null values 2948 assert(value.serializeToJson == `[[],{},""]`, value.serializeToJson); 2949 } 2950 2951 /++ 2952 User defined algebraic types deserialization supports any subset of the following types: 2953 2954 $(UL 2955 $(LI `typeof(null)`) 2956 $(LI `bool`) 2957 $(LI `long`) 2958 $(LI `double`) 2959 $(LI `string`) 2960 $(LI `AnyType[]`) 2961 $(LI `StringMap!AnyType`) 2962 $(LI `AnyType[string]`) 2963 ) 2964 2965 A `StringMap` has has priority over builtin associative arrays. 2966 2967 Serializations works with any algebraic types. 2968 2969 See_also: $(GMREF mir-core, mir,algebraic), $(GMREF mir-algorithm, mir,string_map) 2970 +/ 2971 unittest 2972 { 2973 import mir.algebraic: Nullable, This; // Nullable, Variant, or TaggedVariant 2974 alias MyJsonAlgebraic = Nullable!(bool, string, double[], This[string]); 2975 2976 auto value = `{"b" : true, "z" : null, "this" : {"c" : "str", "d" : [1, 2, 3, 4]}}`.deserialize!MyJsonAlgebraic; 2977 2978 auto object = value.get!(MyJsonAlgebraic[string]); 2979 assert(object["b"].get!bool == true); 2980 assert(object["z"].isNull); 2981 2982 object = object["this"].get!(MyJsonAlgebraic[string]); 2983 assert(object["c"].get!string == "str"); 2984 assert(object["d"].get!(double[]) == [1.0, 2, 3, 4]); 2985 } 2986 2987 /// 2988 unittest 2989 { 2990 static class Turtle 2991 { 2992 string _metadata; 2993 long id; 2994 string species; 2995 } 2996 2997 auto turtles = ` 2998 [{"_metadata":"xyz123", "id":72, "species":"Galapagos"}, 2999 {"_metadata":"tu144", "id":108, "species":"Snapping"}, 3000 null, 3001 null, 3002 {"_metadata":"anew1", "id":9314, "species":"Sea Turtle"}]` 3003 .deserialize!(Turtle[]); 3004 } 3005 3006 /// Alias this support 3007 unittest 3008 { 3009 struct S 3010 { 3011 int a; 3012 } 3013 3014 struct C 3015 { 3016 S s; 3017 alias s this; 3018 int b; 3019 } 3020 3021 assert(`{"a":3, "b":4}`.deserialize!C == C(S(3), 4)); 3022 } 3023 3024 3025 /// `serdeOrderedIn` supprot 3026 unittest 3027 { 3028 static struct I 3029 { 3030 @serdeOptional 3031 int a; 3032 int m; 3033 } 3034 3035 @serdeOrderedIn 3036 static struct S 3037 { 3038 import mir.small_string; 3039 3040 SmallString!8 id; 3041 3042 int acc; 3043 3044 I inner = I(1000, 0); 3045 3046 @safe pure nothrow @nogc 3047 @property: 3048 3049 void add(int v) 3050 { 3051 inner.a += v; 3052 acc += v; 3053 } 3054 3055 void mul(int v) 3056 { 3057 inner.m += v; 3058 acc *= v; 3059 } 3060 } 3061 3062 import mir.reflection; 3063 3064 auto val = `{"mul":2, "id": "str", "add":5,"acc":100, "inner":{"m": 2000}}`.deserialize!S; 3065 assert(val.id == "str"); 3066 assert(val.acc == 210); 3067 assert(val.inner.a == 1005); 3068 assert(val.inner.m == 2002); 3069 assert(val.serializeToJson == `{"id":"str","acc":210,"inner":{"a":1005,"m":2002}}`); 3070 } 3071 3072 /// `serdeRealOrderedIn` supprot 3073 unittest 3074 { 3075 static struct I 3076 { 3077 @serdeOptional 3078 int a; 3079 int m; 3080 } 3081 3082 @serdeRealOrderedIn 3083 static struct S 3084 { 3085 import mir.small_string; 3086 3087 SmallString!8 id; 3088 3089 int acc; 3090 3091 I inner = I(1000, 0); 3092 3093 @safe pure nothrow @nogc 3094 @property: 3095 3096 void add(int v) 3097 { 3098 inner.a += v; 3099 acc += v; 3100 } 3101 3102 void mul(int v) 3103 { 3104 inner.m += v; 3105 acc *= v; 3106 } 3107 } 3108 3109 import mir.reflection; 3110 3111 auto val = `{"mul":2, "id": "str", "add":5,"acc":100, "inner":{"m": 2000}}`.deserialize!S; 3112 assert(val.id == "str"); 3113 assert(val.acc == 210); 3114 assert(val.inner.a == 1005); 3115 assert(val.inner.m == 2002); 3116 assert(val.serializeToJson == `{"id":"str","acc":210,"inner":{"a":1005,"m":2002}}`); 3117 } 3118 3119 /// 3120 unittest 3121 { 3122 struct A { 3123 string str; 3124 } 3125 struct B { 3126 A a; 3127 string serialize() const { 3128 return fghj.serializeToJson(a); 3129 } 3130 } 3131 assert(B(A("2323")).serialize == `{"str":"2323"}`); 3132 } 3133 3134 private template isNullable(T) 3135 { 3136 import std.traits : hasMember; 3137 3138 static if ( 3139 hasMember!(T, "isNull") && 3140 is(typeof(__traits(getMember, T, "isNull")) == bool) && 3141 hasMember!(T, "get") && 3142 !is(typeof(__traits(getMember, T, "get")) == void) && 3143 hasMember!(T, "nullify") && 3144 is(typeof(__traits(getMember, T, "nullify")) == void) 3145 ) 3146 { 3147 enum isNullable = true; 3148 } 3149 else 3150 { 3151 enum isNullable = false; 3152 } 3153 } 3154 3155 deprecated("use @serdeIgnoreOut instead") 3156 alias serializationIgnoreOut = serdeIgnoreOut; 3157 3158 deprecated("use @serdeIgnoreIn instead") 3159 alias serializationIgnoreIn = serdeIgnoreIn; 3160 3161 deprecated("use @serdeIgnore instead") 3162 alias serializationIgnore = serdeIgnore; 3163 3164 deprecated("use @serdeKeys instead") 3165 alias serializationKeys = serdeKeys; 3166 3167 deprecated("use @serdeKeys instead") 3168 alias serializationKeyOut = serdeKeyOut; 3169 3170 deprecated("use @serdeIgnoreDefault instead") 3171 alias serializationIgnoreDefault = serdeIgnoreDefault; 3172 3173 deprecated("use @serdeLikeList instead") 3174 alias serializationLikeArray = serdeLikeList; 3175 3176 deprecated("use @serdeLikeStruct instead") 3177 alias serializationLikeObject = serdeLikeStruct; 3178 3179 deprecated("use @serdeProxy instead") 3180 alias serializedAs = serdeProxy; 3181 3182 deprecated("use @serdeIgnoreOutIf instead") 3183 alias serializationIgnoreOutIf = serdeIgnoreOutIf; 3184 3185 deprecated("use @serdeTransformIn instead") 3186 alias serializationTransformIn = serdeTransformIn; 3187 3188 deprecated("use @serdeTransformOut instead") 3189 alias serializationTransformOut = serdeTransformOut; 3190 3191 deprecated("use @serdeScoped instead") 3192 alias serializationScoped = serdeScoped;