1 /++ 2 DRAFT 3 Macros: 4 AlgorithmREF = $(GREF_ALTTEXT mir-algorithm, $(TT $2), $2, mir, $1)$(NBSP) 5 AAREF = $(REF_ALTTEXT $(TT $2), $2, mir, algebraic_alias, $1)$(NBSP) 6 +/ 7 module mir.ion.examples; 8 9 /// A user may define setter and/or getter properties. 10 @safe pure 11 version(mir_ion_test) unittest 12 { 13 import mir.ser.json; 14 import mir.deser.json; 15 import mir.conv: to; 16 17 static struct S 18 { 19 @serdeIgnore string str; 20 @safe pure: 21 string a() const @property 22 { 23 return str; 24 } 25 26 void b(int s) @property 27 { 28 str = s.to!string; 29 } 30 } 31 32 assert(S("str").serializeJson == `{"a":"str"}`); 33 assert(`{"b":123}`.deserializeJson!S.str == "123"); 34 } 35 36 /// Support for custom nullable types (types that has a bool property `isNull`, 37 /// non-void property `get` returning payload and void property `nullify` that 38 /// makes nullable type to null value) 39 @safe pure 40 version(mir_ion_test) unittest 41 { 42 import mir.ser.json; 43 import mir.deser.json; 44 45 static struct MyNullable 46 { 47 long value; 48 49 @safe pure nothrow @nogc: 50 51 @property 52 isNull() const 53 { 54 return value == 0; 55 } 56 57 @property 58 long get() const 59 { 60 return value; 61 } 62 63 @property 64 nullify() 65 { 66 value = 0; 67 } 68 69 auto opAssign(long value) 70 { 71 this.value = value; 72 } 73 } 74 75 static struct Foo 76 { 77 MyNullable my_nullable; 78 string field; 79 80 bool opEquals()(auto ref const(typeof(this)) rhs) const 81 { 82 if (my_nullable.isNull && rhs.my_nullable.isNull) 83 return field == rhs.field; 84 85 if (my_nullable.isNull != rhs.my_nullable.isNull) 86 return false; 87 88 return my_nullable == rhs.my_nullable && 89 field == rhs.field; 90 } 91 } 92 93 Foo foo; 94 foo.field = "it's a foo"; 95 96 assert (serializeJson(foo) == `{"my_nullable":null,"field":"it's a foo"}`); 97 98 foo.my_nullable = 200; 99 100 assert (deserializeJson!Foo(`{"my_nullable":200,"field":"it's a foo"}`) == Foo(MyNullable(200), "it's a foo")); 101 102 import mir.algebraic: Nullable; 103 104 static struct Bar 105 { 106 Nullable!long nullable; 107 string field; 108 109 bool opEquals()(auto ref const(typeof(this)) rhs) 110 { 111 if (nullable.isNull && rhs.nullable.isNull) 112 return field == rhs.field; 113 114 if (nullable.isNull != rhs.nullable.isNull) 115 return false; 116 117 return nullable == rhs.nullable && 118 field == rhs.field; 119 } 120 } 121 122 Bar bar; 123 bar.field = "it's a bar"; 124 125 assert (serializeJson(bar) == `{"nullable":null,"field":"it's a bar"}`); 126 127 bar.nullable = 777; 128 assert (deserializeJson!Bar(`{"nullable":777,"field":"it's a bar"}`) == Bar(Nullable!long(777), "it's a bar")); 129 } 130 131 /// 132 @safe pure 133 version(mir_ion_test) unittest 134 { 135 import mir.ser.ion; 136 import mir.ser.json; 137 import mir.ion.stream; 138 139 IonValueStream[string] map; 140 141 map["num"] = IonValueStream(serializeIon(124)); 142 map["str"] = IonValueStream(serializeIon("value")); 143 144 auto json = map.serializeJson; 145 assert(json == `{"str":"value","num":124}` || json == `{"num":124,"str":"value"}`); 146 } 147 148 /// Support for floating point nan and (partial) infinity 149 version(mir_ion_test) unittest 150 { 151 import mir.conv: to; 152 import mir.ser.ion; 153 import mir.ser.json; 154 import mir.deser.json; 155 import mir.ion.conv; 156 157 static struct Foo 158 { 159 float f; 160 161 bool opEquals()(auto ref const(typeof(this)) rhs) 162 { 163 return f != f && rhs.f != rhs.f || f == rhs.f; 164 } 165 } 166 167 // test for Not a Number 168 assert (serializeJson(Foo()) == `{"f":"nan"}`, serializeJson(Foo())); 169 assert (serializeIon(Foo()).ion2json == `{"f":"nan"}`, serializeIon(Foo()).ion2json); 170 171 assert (deserializeJson!Foo(`{"f":"nan"}`) == Foo(), deserializeJson!Foo(`{"f":"nan"}`).to!string); 172 173 assert (serializeJson(Foo(1f/0f)) == `{"f":"+inf"}`); 174 assert (serializeIon(Foo(1f/0f)).ion2json == `{"f":"+inf"}`); 175 assert (deserializeJson!Foo(`{"f":"+inf"}`) == Foo( float.infinity)); 176 assert (deserializeJson!Foo(`{"f":"-inf"}`) == Foo(-float.infinity)); 177 178 assert (serializeJson(Foo(-1f/0f)) == `{"f":"-inf"}`); 179 assert (serializeIon(Foo(-1f/0f)).ion2json == `{"f":"-inf"}`); 180 assert (deserializeJson!Foo(`{"f":"-inf"}`) == Foo(-float.infinity)); 181 } 182 183 /// 184 version(mir_ion_test) unittest 185 { 186 import mir.ser.ion; 187 import mir.ser.json; 188 import mir.deser.json; 189 import mir.ion.conv; 190 191 static struct S 192 { 193 string foo; 194 uint bar; 195 } 196 197 static immutable json = `{"foo":"str","bar":4}`; 198 assert(serializeIon(S("str", 4)).ion2json == json); 199 assert(serializeJson(S("str", 4)) == json); 200 assert(deserializeJson!S(json) == S("str", 4)); 201 } 202 203 /// Proxy for members 204 version(mir_ion_test) unittest 205 { 206 import mir.ser.json; 207 import mir.deser.json; 208 209 struct S 210 { 211 // const(char)[] doesn't reallocate ASDF data. 212 @serdeProxy!(const(char)[]) 213 uint bar; 214 } 215 216 auto json = `{"bar":"4"}`; 217 assert(serializeJson(S(4)) == json); 218 assert(deserializeJson!S(json) == S(4)); 219 } 220 221 /// 222 pure version(mir_ion_test) unittest 223 { 224 import mir.ser.json; 225 import mir.deser.json; 226 import mir.serde: serdeKeys; 227 static struct S 228 { 229 @serdeKeys("b", "a") 230 string s; 231 } 232 assert(`{"a":"d"}`.deserializeJson!S.serializeJson == `{"b":"d"}`); 233 } 234 235 /// 236 pure version(mir_ion_test) unittest 237 { 238 import mir.ser.json; 239 import mir.deser.json; 240 import mir.serde: serdeKeys, serdeKeyOut; 241 static struct S 242 { 243 @serdeKeys("a") 244 @serdeKeyOut("s") 245 string s; 246 } 247 assert(`{"a":"d"}`.deserializeJson!S.serializeJson == `{"s":"d"}`); 248 } 249 250 /// 251 pure version(mir_ion_test) unittest 252 { 253 import mir.ser.json; 254 import mir.deser.json; 255 import std.exception: assertThrown; 256 257 struct S 258 { 259 string field; 260 } 261 262 assert(`{"field":"val"}`.deserializeJson!S.field == "val"); 263 // assertThrown(`{"other":"val"}`.deserializeJson!S); 264 } 265 266 /// 267 version(mir_ion_test) unittest 268 { 269 import mir.ser.json; 270 import mir.deser.json; 271 272 static struct S 273 { 274 @serdeKeyOut("a") 275 string s; 276 } 277 assert(`{"s":"d"}`.deserializeJson!S.serializeJson == `{"a":"d"}`); 278 } 279 280 /// 281 version(mir_ion_test) unittest 282 { 283 import mir.ser.json; 284 import mir.deser.json; 285 import std.exception: assertThrown; 286 287 static struct S 288 { 289 @serdeIgnore 290 string s; 291 } 292 // assertThrown(`{"s":"d"}`.deserializeJson!S); 293 assert(S("d").serializeJson == `{}`); 294 } 295 296 /// 297 version(mir_ion_test) unittest 298 { 299 import mir.ser.json; 300 import mir.deser.json; 301 302 static struct Decor 303 { 304 int candles; // 0 305 float fluff = float.infinity; // inf 306 } 307 308 static struct Cake 309 { 310 @serdeIgnoreDefault string name = "Chocolate Cake"; 311 @serdeIgnoreDefault string nullStr; 312 @serdeIgnoreDefault string emptyStr = ""; 313 int slices = 8; 314 float flavor = 1; 315 @serdeIgnoreDefault 316 Decor dec = Decor(20); // { 20, inf } 317 } 318 319 assert(Cake("Normal Cake", "", null).serializeJson == `{"name":"Normal Cake","slices":8,"flavor":1.0}`); 320 auto cake = Cake.init; 321 cake.dec = Decor.init; 322 assert(cake.serializeJson == `{"slices":8,"flavor":1.0,"dec":{"candles":0,"fluff":"+inf"}}`); 323 assert(cake.dec.serializeJson == `{"candles":0,"fluff":"+inf"}`); 324 325 static struct A 326 { 327 @serdeIgnoreDefault 328 string str = "Banana"; 329 int i = 1; 330 } 331 assert(A.init.serializeJson == `{"i":1}`); 332 333 static struct S 334 { 335 @serdeIgnoreDefault 336 A a; 337 } 338 assert(S.init.serializeJson == `{}`); 339 assert(S(A("Berry")).serializeJson == `{"a":{"str":"Berry","i":1}}`); 340 341 static struct D 342 { 343 S s; 344 } 345 assert(D.init.serializeJson == `{"s":{}}`); 346 assert(D(S(A("Berry"))).serializeJson == `{"s":{"a":{"str":"Berry","i":1}}}`); 347 assert(D(S(A(null, 0))).serializeJson == `{"s":{"a":{"str":"","i":0}}}`); 348 349 static struct F 350 { 351 D d; 352 } 353 assert(F.init.serializeJson == `{"d":{"s":{}}}`); 354 } 355 356 /// 357 version(mir_ion_test) unittest 358 { 359 import mir.ser.json; 360 import mir.deser.json; 361 import mir.serde: serdeIgnoreOut; 362 363 static struct S 364 { 365 @serdeIgnoreOut 366 string s; 367 } 368 assert(`{"s":"d"}`.deserializeJson!S.s == "d"); 369 assert(S("d").serializeJson == `{}`); 370 } 371 372 /// 373 version(mir_ion_test) unittest 374 { 375 import mir.ser.json; 376 import mir.deser.json; 377 378 static struct CustomType 379 { 380 int a; 381 382 // can be part of a type as well (only omits on property keys) 383 auto serdeIgnoreOut() const 384 { 385 return a < 0; 386 } 387 388 // and use custom serializer to serialize as int 389 void serialize(S)(scope ref S serializer) scope const 390 { 391 import mir.ser : serializeValue; 392 393 serializeValue(serializer, a); 394 } 395 } 396 397 static struct S 398 { 399 @serdeIgnoreOutIf!`a < 0` 400 int a; 401 402 // b is not output if the serdeIgnoreOut property in its type evaluates to false. 403 CustomType b; 404 } 405 406 assert(serializeJson(S(3, CustomType(2))) == `{"a":3,"b":2}`, serializeJson(S(3, CustomType(2)))); 407 assert(serializeJson(S(-3, CustomType(-2))) == `{}`); 408 } 409 410 /// 411 @safe pure 412 version(mir_ion_test) unittest 413 { 414 import mir.ser.json; 415 import mir.deser.json; 416 417 import std.uuid: UUID; 418 419 static struct S 420 { 421 @serdeScoped 422 @serdeProxy!string 423 UUID id; 424 } 425 426 enum result = UUID("8AB3060E-2cba-4f23-b74c-b52db3bdfb46"); 427 assert(`{"id":"8AB3060E-2cba-4f23-b74c-b52db3bdfb46"}`.deserializeJson!S.id == result); 428 } 429 430 /// 431 version(mir_ion_test) unittest 432 { 433 import mir.ser.json; 434 import mir.deser.json; 435 import mir.ion.value; 436 import mir.algebraic: Variant; 437 438 static struct ObjectA 439 { 440 string name; 441 } 442 static struct ObjectB 443 { 444 double value; 445 } 446 447 alias MyObject = Variant!(ObjectA, ObjectB); 448 449 static struct MyObjectArrayProxy 450 { 451 MyObject[] array; 452 453 this(inout(MyObject)[] array) @safe pure nothrow @nogc inout 454 { 455 this.array = array; 456 } 457 458 inout(T) opCast(T : MyObject[])() inout 459 { 460 return array; 461 } 462 463 void serialize(S)(scope ref S serializer) scope const 464 { 465 import mir.ser: serializeValue; 466 // mir.algebraic has builtin support for serialization. 467 // For other algebraic libraies one can use thier visitor handlers. 468 serializeValue(serializer, array); 469 } 470 471 /++ 472 Returns: error msg if any 473 +/ 474 @trusted pure scope 475 IonException deserializeFromIon(scope const char[][] symbolTable, scope IonDescribedValue value) 476 { 477 import mir.deser.ion: deserializeIon; 478 import mir.ion.exception; 479 foreach (IonErrorCode error, scope IonDescribedValue elem; value.get!IonList) 480 { 481 if (error) 482 return error.ionException; 483 array ~= "name" in elem.get!IonStruct.withSymbols(symbolTable) 484 ? MyObject(deserializeIon!ObjectA(symbolTable, elem)) 485 : MyObject(deserializeIon!ObjectB(symbolTable, elem)); 486 } 487 return null; 488 } 489 } 490 491 static struct SomeObject 492 { 493 @serdeProxy!MyObjectArrayProxy MyObject[] objects; 494 } 495 496 string data = q{{"objects":[{"name":"test"},{"value":1.5}]}}; 497 498 auto value = data.deserializeJson!SomeObject; 499 assert (value.serializeJson == data, value.serializeJson); 500 } 501 502 // TODO 503 // version(none) 504 // unittest 505 // { 506 // Asdf[string] map; 507 508 // map["num"] = serializeToAsdf(124); 509 // map["str"] = serializeToAsdf("value"); 510 511 // import std.stdio; 512 // map.serializeToJson.writeln(); 513 // } 514 515 // TODO 516 // version(none) 517 // unittest 518 // { 519 // import mir.algebraic: Variant, Nullable, This; 520 // alias V = Nullable!(double, string, This[], This[string]); 521 // V v; 522 // assert(v.serializeToJson == "null", v.serializeToJson); 523 // v = [V(2), V("str"), V(["key":V(1.0)])]; 524 // assert(v.serializeToJson == `[2.0,"str",{"key":1.0}]`); 525 // } 526 527 /// reflectSerde support 528 version(mir_ion_test) unittest 529 { 530 import mir.reflection: reflectSerde; 531 static struct S 532 { 533 @reflectSerde enum s = "str"; 534 } 535 536 import mir.ser.text: serializeText; 537 assert(S().serializeText == `{s:"str"}`); 538 } 539 540 /// serdeIgnoreUnexpectedKeys support 541 version(mir_ion_test) unittest 542 { 543 import mir.serde: serdeIgnoreUnexpectedKeys; 544 @serdeIgnoreUnexpectedKeys 545 static struct S { int i; } 546 547 import mir.deser.text: deserializeText; 548 assert(`{s:"str",i:4}`.deserializeText!S == S(4)); 549 } 550 551 /// Iterable and serdeLikeList 552 version(mir_ion_test) unittest 553 { 554 import mir.deser.text: deserializeText; 555 import mir.ser.text: serializeText; 556 import mir.serde: serdeIgnoreOut, serdeLikeList, serdeProxy, serdeKeys; 557 import std.array: Appender; 558 559 static struct S 560 { 561 @serdeLikeList 562 @serdeProxy!string // input element type of 563 @serdeIgnoreOut 564 @serdeKeys("strings") 565 Appender!(string[]) stringsApp; //`put` method is used 566 567 this(const(string)[] strings) 568 { 569 stringsApp.put(strings); 570 } 571 572 auto strings() const @property // Iterable 573 { 574 import mir.ndslice.topology: map; 575 return stringsApp.data.map!((string s) => "_" ~ s); 576 } 577 578 } 579 580 assert(S([`a`, `b`]).serializeText == `{strings:["_a","_b"]}`); 581 import mir.test; 582 `{strings:["a","b"]}`.deserializeText!S.stringsApp.data.should == [`a`, `b`]; 583 584 @serdeLikeList 585 @serdeProxy!string // input element type of 586 static struct C 587 { 588 /// Used to make S deserializable 589 Appender!(string[]) stringsApp; //`put` method is used 590 alias stringsApp this; 591 592 this(const(string)[] strings) 593 { 594 stringsApp.put(strings); 595 } 596 597 /// To make C iterable and serializable 598 auto opIndex() const { return stringsApp.data; } 599 } 600 601 static struct S2 { 602 static prop() @property { return "100"; }; 603 } 604 605 assert(S2().serializeText == `{prop:"100"}`, S2().serializeText); 606 assert(C([`a`, `b`]).serializeText == `["a","b"]`); 607 assert(`["a","b"]`.deserializeText!C.stringsApp.data == [`a`, `b`]); 608 } 609 610 /// serdeLikeStruct 611 unittest 612 { 613 import mir.test: should; 614 import mir.ser.text: serializeText; 615 import mir.deser.text: deserializeText; 616 import mir.serde: serdeLikeStruct, serdeProxy; 617 618 // opApply for serialization & and opIndexAssign for deserialization 619 static struct M 620 { 621 private int sum; 622 623 // opApply is used for serialization 624 int opApply(scope int delegate(scope const char[] key, ref const int val) pure @safe dg) const pure @safe 625 { 626 { int var = 1; if (auto r = dg("a", var)) return r; } 627 { int var = 2; if (auto r = dg("b", var)) return r; } 628 { int var = 3; if (auto r = dg("c", var)) return r; } 629 return 0; 630 } 631 632 // opIndexAssign for deserialization 633 void opIndexAssign(int val, string key) pure @safe 634 { 635 sum += val; 636 } 637 } 638 639 // attribute on member + opApply 640 static struct S 641 { 642 @serdeLikeStruct 643 @serdeProxy!int // for deserialization 644 M obj; 645 } 646 647 assert(S().serializeText == `{obj:{a:1,b:2,c:3}}`); 648 `{obj:{a:1,b:2,c:3}}`.deserializeText!S.obj.sum.should == 6; 649 650 // attribute on type + opApply 651 @serdeLikeStruct 652 @serdeProxy!int // for deserialization 653 static struct C 654 { 655 M obj; 656 alias obj this; 657 } 658 659 assert(C().serializeText == `{a:1,b:2,c:3}`); 660 assert(`{a:1,b:2,c:3}`.deserializeText!C.obj.sum == 6); 661 662 // byKeyValue 663 static struct D 664 { 665 static struct KeyValue 666 { 667 string key; 668 int value; 669 } 670 KeyValue[] byKeyValue = [KeyValue("a", 1), KeyValue("b", 2), KeyValue("c", 3)]; 671 } 672 673 // attribute on member + byKeyValue 674 static struct F 675 { 676 @serdeLikeStruct 677 D obj; 678 } 679 680 assert(F().serializeText == `{obj:{a:1,b:2,c:3}}`); 681 682 // attribute on type + byKeyValue 683 @serdeLikeStruct 684 static struct B 685 { 686 D obj; 687 alias obj this; 688 } 689 690 assert(B().serializeText == `{a:1,b:2,c:3}`); 691 } 692 693 /// 694 version(mir_ion_test) unittest 695 { 696 import mir.ser.json; 697 import mir.deser.json; 698 import std.range; 699 import std.algorithm; 700 import std.conv; 701 702 static struct S 703 { 704 @serdeTransformIn!"a += 2" 705 @serdeTransformOut!(a =>"str".repeat.take(a).joiner("_").to!string) 706 int a; 707 } 708 709 auto s = deserializeJson!S(`{"a":3}`); 710 assert(s.a == 5); 711 assert(serializeJson(s) == `{"a":"str_str_str_str_str"}`); 712 } 713 714 /// 715 @safe pure 716 version(mir_ion_test) unittest 717 { 718 static struct Toto 719 { 720 import mir.ndslice.slice: Slice; 721 722 Slice!(int*, 2) a; 723 Slice!(int*, 1) b; 724 725 this(int x) @safe pure nothrow 726 { 727 import mir.ndslice.topology: iota, repeat; 728 import mir.ndslice.allocation: slice; 729 730 a = [2, 3].iota!int.slice; 731 b = repeat(x, [2]).slice; 732 } 733 } 734 735 auto toto = Toto(5); 736 737 import mir.ser.json: serializeJson; 738 import mir.deser.json: deserializeJson; 739 740 auto description = toto.serializeJson!Toto; 741 assert(description == q{{"a":[[0,1,2],[3,4,5]],"b":[5,5]}}); 742 assert(toto == description.deserializeJson!Toto); 743 } 744 745 /// 746 @safe pure @nogc 747 version(mir_ion_test) unittest 748 { 749 static struct Toto 750 { 751 import mir.ndslice.slice: Slice; 752 import mir.rc.array: RCI; 753 754 Slice!(RCI!int, 2) a; 755 Slice!(RCI!int, 1) b; 756 757 this(int x) @safe pure nothrow @nogc 758 { 759 import mir.ndslice.topology: iota, repeat; 760 import mir.ndslice.allocation: rcslice; 761 762 a = [2, 3].iota!int.rcslice; 763 b = repeat(x, [2]).rcslice; 764 } 765 } 766 767 auto toto = Toto(5); 768 769 import mir.ser.json: serializeJson; 770 import mir.deser.json: deserializeJson; 771 import mir.format: stringBuf; 772 773 auto buffer = stringBuf; 774 serializeJson(buffer, toto); 775 auto description = buffer.data; 776 assert(description == q{{"a":[[0,1,2],[3,4,5]],"b":[5,5]}}); 777 assert(toto == description.deserializeJson!Toto); 778 } 779 780 /++ 781 User defined algebraic types deserialization supports any subset of the following types: 782 783 $(UL 784 $(LI `typeof(null)`) 785 $(LI `bool`) 786 $(LI `long`) 787 $(LI `double`) 788 $(LI `string`) 789 $(LI $(AlgorithmREF timestamp, Timestamp)) 790 $(LI `AnyType[]`) 791 $(LI `StringMap!AnyType`) 792 $(LI `AnyType[string]`) 793 ) 794 795 A `StringMap` has has priority over builtin associative arrays. 796 797 Serializations works with any algebraic types. 798 799 See_also: $(GMREF mir-core, mir,algebraic), $(GMREF mir-algorithm, mir,string_map) 800 +/ 801 version(mir_ion_test) unittest 802 { 803 import mir.string_map; 804 import mir.deser.ion: deserializeIon; 805 import mir.ion.conv: json2ion, ion2text; 806 import mir.algebraic: Algebraic, This; 807 808 static union Json_ 809 { 810 typeof(null) null_; 811 bool boolean; 812 long integer; 813 immutable(char)[] string; 814 double[] array; 815 StringMap!This object; 816 } 817 818 alias MyJsonAlgebraic = Algebraic!Json_; 819 820 auto json = `{"b" : true, "z" : null, "this" : {"c" : "str", "d" : [1, 2, 3, 4]}}`; 821 auto binary = json.json2ion; 822 auto value = binary.deserializeIon!MyJsonAlgebraic; 823 824 auto object = value.get!(StringMap!MyJsonAlgebraic); 825 assert(object["b"].get!bool == true); 826 assert(object["z"].isNull); 827 828 object = object["this"].get!(StringMap!MyJsonAlgebraic); 829 assert(object["c"].get!string == "str"); 830 assert(object["d"].get!(double[]) == [1.0, 2, 3, 4]); 831 } 832 833 /// 834 version(mir_ion_test) unittest 835 { 836 import mir.algebraic_alias.json; 837 import mir.ser.json: serializeJson; 838 auto value = [JsonAlgebraic[].init.JsonAlgebraic, StringMap!JsonAlgebraic.init.JsonAlgebraic, string.init.JsonAlgebraic]; 839 assert(value.serializeJson == `[[],{},""]`, value.serializeJson); 840 } 841 842 /// 843 version(mir_ion_test) @safe pure unittest 844 { 845 import mir.test; 846 import mir.serde : serdeFallbackStruct; 847 import mir.algebraic; 848 import mir.deser.text; 849 import mir.deser.json; 850 @serdeFallbackStruct struct S { string path; } 851 alias V = Variant!(string, S); 852 static immutable res = [V("str"), V(S("root"))]; 853 q{["str", {path: root}]}.deserializeText!(V[]).should == res; 854 q{ [ "str" , { "path" : "root" } ] }.deserializeJson!(V[]).should == res; 855 } 856 857 /// Date serialization 858 version(mir_ion_test) unittest 859 { 860 import mir.date; 861 import mir.ion.conv: ion2text; 862 import mir.ser.ion: serializeIon; 863 import mir.ser.json: serializeJson; 864 import mir.ser.text: serializeText; 865 assert(Date(2021, 4, 24).serializeIon.ion2text == `2021-04-24`); 866 assert(Date(2021, 4, 24).serializeText == `2021-04-24`); 867 assert(Date(2021, 4, 24).serializeJson == `"2021-04-24"`); 868 } 869 870 /// Timestamp and LOB support in algebraic types 871 version(mir_ion_test) unittest 872 { 873 import mir.algebraic; 874 import mir.deser.ion: deserializeIon; 875 import mir.ser.ion: serializeIon; 876 import mir.lob; 877 import mir.string_map; 878 import mir.timestamp; 879 880 static union Ion_ 881 { 882 typeof(null) null_; 883 bool boolean; 884 long integer; 885 double float_; 886 immutable(char)[] string; 887 Blob blob; 888 Clob clob; 889 Timestamp timestamp; 890 This[] array; 891 StringMap!This object; 892 } 893 894 alias IonAlgebraic = Algebraic!Ion_; 895 896 StringMap!IonAlgebraic map; 897 map["ts"] = Timestamp(2021, 4, 24); 898 map["clob"] = Clob("Some clob"); 899 map["blob"] = Blob([0x32, 0x52]); 900 901 assert(map.serializeIon.deserializeIon!(StringMap!IonAlgebraic) == map); 902 } 903 904 /// Phobos date-time serialization 905 version(mir_ion_test) unittest 906 { 907 import core.time : hnsecs, minutes; 908 import mir.ion.conv: ion2text; 909 import mir.ser.ion: serializeIon; 910 import mir.ser.json: serializeJson; 911 import mir.ser.text: serializeText; 912 import mir.deser.ion: deserializeIon; 913 import mir.deser.json: deserializeJson; 914 import std.datetime.date : Date, DateTime; 915 import std.datetime.systime : SysTime; 916 import std.datetime.timezone : SimpleTimeZone; 917 918 auto date = Date(2021, 4, 24); 919 assert(date.serializeIon.ion2text == `2021-04-24`); 920 assert(date.serializeText == `2021-04-24`); 921 assert(date.serializeJson == `"2021-04-24"`); 922 assert(`"2021-04-24"`.deserializeJson!Date == date); 923 924 auto datetime = DateTime(1982, 4, 1, 20, 59, 22); 925 assert(datetime.serializeIon.ion2text == `1982-04-01T20:59:22-00:00`); 926 assert(datetime.serializeText == `1982-04-01T20:59:22-00:00`); 927 assert(datetime.serializeJson == `"1982-04-01T20:59:22-00:00"`); 928 assert(`"1982-04-01T20:59:22Z"`.deserializeJson!DateTime == datetime); 929 930 auto dt = DateTime(1982, 4, 1, 20, 59, 22); 931 auto tz = new immutable SimpleTimeZone(-330.minutes); 932 auto st = SysTime(dt, 1234567.hnsecs, tz); 933 assert(st.serializeIon.ion2text == `1982-04-01T20:59:22.1234567-05:30`); 934 assert(st.serializeText == `1982-04-01T20:59:22.1234567-05:30`); 935 assert(st.serializeJson == `"1982-04-01T20:59:22.1234567-05:30"`); 936 assert(st.serializeIon.deserializeIon!SysTime == st); 937 assert(`"1982-04-01T20:59:22.1234567-05:30"`.deserializeJson!SysTime == st); 938 } 939 940 version(mir_ion_test) unittest 941 { 942 import mir.deser.json; 943 auto s = q{"n2. Clone theproject_n_n git clone git://github.com/rej\n2. Clone the project_n_n git clone git://github.com/rejn"}.deserializeJson!string; 944 assert(s == "n2. Clone theproject_n_n git clone git://github.com/rej\n2. Clone the project_n_n git clone git://github.com/rejn", s); 945 } 946 947 version(mir_ion_test) unittest 948 { 949 import mir.deser.json; 950 assert(q{"n2. Clone theproject_n_n git clone git://github.com/rej\"). Clone the project_n_n git clone git://github.com/rejn"} 951 .deserializeJson!string == 952 "n2. Clone theproject_n_n git clone git://github.com/rej\"). Clone the project_n_n git clone git://github.com/rejn"); 953 } 954 955 version (mir_ion_test) 956 unittest 957 { 958 import mir.test; 959 import mir.deser.json; 960 `["\u007F"]`.deserializeJson!(string[]).should == ["\u007F"]; 961 } 962 963 version (mir_ion_test) 964 unittest 965 { 966 import mir.test; 967 import mir.deser.json; 968 `["\uD801\uDC37"]`.deserializeJson!(string[]).should == ["\U00010437"]; 969 } 970 971 /// Static array support 972 @safe pure 973 version(mir_ion_test) unittest 974 { 975 import mir.ser.json; 976 import mir.deser.json; 977 int[3] value = [1, 2, 3]; 978 assert(`[1,2,3]`.deserializeJson!(int[3]) == value); 979 assert(value.serializeJson == `[1,2,3]`); 980 } 981 982 /// ditto 983 @safe pure 984 version(mir_ion_test) unittest 985 { 986 import mir.ser.json; 987 import mir.deser.json; 988 static struct S { int v; } 989 S[3] value = [S(1), S(2), S(3)]; 990 auto text = `[{"v":1},{"v":2},{"v":3}]`; 991 assert(text.deserializeJson!(S[3]) == value); 992 assert(value.serializeJson == text); 993 } 994 995 // AliasThis and serdeProxy 996 version(mir_ion_test) unittest 997 { 998 @serdeProxy!(int[3]) 999 static struct J 1000 { 1001 int[3] ar; 1002 alias ar this; 1003 } 1004 1005 import mir.ser.json; 1006 import mir.deser.json; 1007 auto value = J([1, 2, 3]); 1008 assert(value.serializeJson == `[1,2,3]`); 1009 assert(`[1,2,3]`.deserializeJson!J == value); 1010 } 1011 1012 1013 version(mir_ion_test) unittest 1014 { 1015 import mir.ser.text; 1016 1017 static struct A { 1018 private int _a; 1019 const @property: 1020 int a() @safe pure { return _a; } 1021 } 1022 1023 auto stra = A(3); 1024 1025 assert(stra.serializeText == `{a:3}`, stra.serializeText); 1026 } 1027 1028 /// 1029 version(mir_ion_test) unittest 1030 { 1031 auto json = q{{ 1032 "a": [ 1033 0.0031 1034 ], 1035 "b": [ 1036 0.999475, 1037 0.999425 1038 ] 1039 }}; 1040 import mir.deser.json; 1041 static struct S { 1042 double[] a; 1043 1044 void serdeUnexpectedKeyHandler(scope const(char)[] key) scope @safe pure nothrow @nogc 1045 { 1046 assert(key == "b"); 1047 } 1048 } 1049 auto value = json.deserializeJson!(double[][string]); 1050 auto partialStruct = json.deserializeJson!S; 1051 } 1052 1053 /// 1054 version(mir_ion_test) unittest 1055 { 1056 auto json = q{{ 1057 "index": 0.9962125, 1058 "data": 0.0001 1059 }}; 1060 1061 import mir.deser.json; 1062 import mir.series; 1063 1064 alias T = Observation!(double, double); 1065 auto value = json.deserializeJson!T; 1066 assert(value.index == 0.9962125); 1067 assert(value.data == 0.0001); 1068 } 1069 1070 /// 1071 version(mir_ion_test) unittest 1072 { 1073 static class MyHugeRESTString 1074 { 1075 string s; 1076 1077 this(string s) @safe pure nothrow @nogc 1078 { 1079 this.s = s; 1080 } 1081 1082 void serialize(S)(scope ref S serializer) scope const 1083 { 1084 auto state = serializer.stringBegin; 1085 // putStringPart is usefull in the loop and with buffers 1086 serializer.putStringPart(s); 1087 serializer.putStringPart(" Another chunk."); 1088 serializer.stringEnd(state); 1089 } 1090 @safe const scope pure nothrow @nogc: 1091 1092 bool opEquals(scope const typeof(this) rhs) 1093 { 1094 return s == rhs.s; 1095 } 1096 1097 int opCmp(scope const typeof(this) rhs) 1098 { 1099 return __cmp(s, rhs.s); 1100 } 1101 1102 override size_t toHash() 1103 { 1104 return hashOf(s); 1105 } 1106 } 1107 1108 import mir.algebraic: Algebraic, This; 1109 import mir.string_map; 1110 1111 // Your JSON DOM Type 1112 static union Json_ 1113 { 1114 typeof(null) null_; 1115 bool boolean; 1116 long integer; 1117 double float_; 1118 immutable(char)[] string; 1119 MyHugeRESTString restString; 1120 This[] array; 1121 StringMap!This object; 1122 } 1123 alias Json = Algebraic!Json_; 1124 1125 /// ordered 1126 StringMap!Json response; 1127 response["type"] = Json("response"); 1128 response["data"] = Json(new MyHugeRESTString("First chunk.")); 1129 1130 import mir.ion.conv: ion2text; 1131 import mir.ser.ion; 1132 import mir.ser.json; 1133 import mir.ser.text; 1134 1135 assert(response.serializeJson == `{"type":"response","data":"First chunk. Another chunk."}`); 1136 assert(response.serializeText == `{type:"response",data:"First chunk. Another chunk."}`); 1137 assert(response.serializeIon.ion2text == `{type:"response",data:"First chunk. Another chunk."}`); 1138 } 1139 1140 /// Float proxy for integer types 1141 version(mir_ion_test) unittest 1142 { 1143 import mir.deser.json; 1144 static struct S { 1145 @serdeProxy!double 1146 long integralValue; 1147 } 1148 1149 assert(`{"integralValue":1.23e+2}`.deserializeJson!S.integralValue == 123); 1150 } 1151 1152 /// Annotated variant 1153 version(mir_ion_test) unittest 1154 { 1155 import mir.algebraic; 1156 import mir.deser.json; 1157 import mir.deser.text; 1158 import mir.ser.json; 1159 import mir.ser.text; 1160 1161 @serdeAlgebraicAnnotation("s1") 1162 static struct S1 { 1163 string key; 1164 } 1165 1166 @serdeAlgebraicAnnotation("s2") 1167 static struct S2 { 1168 long key; 1169 } 1170 1171 alias V = Variant!(S1, S2); 1172 auto v = [V(123), V("124")]; 1173 1174 auto ion = `[s2::{key:123},s1::{key:"124"}]`; 1175 auto json = `[{"s2":{"key":123}},{"s1":{"key":"124"}}]`; 1176 assert(v.serializeText == ion); 1177 assert(ion.deserializeText!(V[2]) == v); 1178 assert(v.serializeJson == json); 1179 assert(json.deserializeJson!(V[2]) == v); 1180 } 1181 1182 /// Annotated arrays 1183 version(mir_ion_test) unittest 1184 { 1185 import mir.algebraic; 1186 import mir.deser.ion; 1187 import mir.ser.ion; 1188 import mir.ser.text; 1189 import mir.serde; 1190 1191 @serdeAlgebraicAnnotation("annotation") 1192 @serdeProxy!(long[]) 1193 static struct St3 { 1194 long[] arr; 1195 alias arr this; 1196 } 1197 alias V = Variant!St3; 1198 1199 assert(V(St3([123])).serializeText == `annotation::[123]`); 1200 assert(V(St3([123])).serializeIon.deserializeIon!V == St3([123])); 1201 } 1202 1203 version(mir_ion_test) @nogc unittest 1204 { 1205 import mir.ion.conv: text2ion; 1206 import mir.deser.ion; 1207 import mir.serde; 1208 1209 @serdeScoped 1210 @serdeProxy!(const(char)[]) 1211 static struct S 1212 { 1213 bool r; 1214 this(scope const(char)[] s) @safe pure nothrow @nogc 1215 { 1216 this.r = s == `simpleSymbol`; 1217 } 1218 } 1219 static immutable data = `simpleSymbol`.text2ion; 1220 assert(data.deserializeIon!S.r); 1221 } 1222 1223 version(unittest) private 1224 { 1225 import mir.serde: serdeProxy; 1226 1227 @serdeProxy!ProxyE 1228 enum E 1229 { 1230 none, 1231 bar, 1232 } 1233 1234 // const(char)[] doesn't reallocate ASDF data. 1235 @serdeProxy!(const(char)[]) 1236 struct ProxyE 1237 { 1238 E e; 1239 1240 @safe pure: 1241 1242 this(E e) 1243 { 1244 this.e = e; 1245 } 1246 1247 this(in char[] str) @trusted 1248 { 1249 switch(str) 1250 { 1251 case "NONE": 1252 case "NA": 1253 case "N/A": 1254 e = E.none; 1255 break; 1256 case "BAR": 1257 case "BR": 1258 e = E.bar; 1259 break; 1260 default: 1261 throw new Exception("Unknown: " ~ cast(string)str); 1262 } 1263 } 1264 1265 string toString() scope const 1266 { 1267 if (e == E.none) 1268 return "NONE"; 1269 else 1270 return "BAR"; 1271 } 1272 1273 E opCast(T : E)() 1274 { 1275 return e; 1276 } 1277 } 1278 1279 version(mir_ion_test) unittest 1280 { 1281 import mir.ser.json; 1282 import mir.deser.json; 1283 1284 assert(serializeJson(E.bar) == `"BAR"`); 1285 assert(`"N/A"`.deserializeJson!E == E.none); 1286 assert(`"NA"`.deserializeJson!E == E.none); 1287 } 1288 } 1289 1290 /// enums 1291 version(mir_ion_test) unittest 1292 { 1293 import std.exception : assertThrown; 1294 import mir.serde: serdeProxy; 1295 1296 enum Builtin 1297 { 1298 one = 1, 1299 two = 2 1300 } 1301 1302 enum BuiltinString : string 1303 { 1304 one = "eins", 1305 two = "zwei" 1306 } 1307 1308 @serdeProxy!int 1309 enum ByInt 1310 { 1311 one = 1, 1312 two = 2 1313 } 1314 1315 @serdeProxy!string 1316 enum StringEnum : string 1317 { 1318 one = "eins", 1319 two = "zwei" 1320 } 1321 1322 static struct B 1323 { 1324 Builtin builtin; 1325 BuiltinString bstr; 1326 } 1327 1328 static struct P 1329 { 1330 ByInt byInt; 1331 StringEnum str; 1332 } 1333 1334 import mir.ser.json : serializeJson; 1335 import mir.deser.json : deserializeJson; 1336 1337 assert(`{"builtin":"one","bstr":"one"}`.deserializeJson!B 1338 == B(Builtin.one, BuiltinString.one)); 1339 assert(`{"builtin":"two","bstr":"two"}`.deserializeJson!B 1340 == B(Builtin.two, BuiltinString.two)); 1341 assertThrown(`{"builtin":"three","bstr":"three"}`.deserializeJson!B); 1342 1343 assert(`{"byInt":1,"str":"eins"}`.deserializeJson!P 1344 == P(ByInt.one, StringEnum.one)); 1345 assert(`{"byInt":2,"str":"zwei"}`.deserializeJson!P 1346 == P(ByInt.two, StringEnum.two)); 1347 assertThrown(`{"byInt":2,"str":"drei"}`.deserializeJson!P); 1348 // asserts are a little inconsistent with integral values 1349 assert(`{"byInt":3,"str":"zwei"}`.deserializeJson!P 1350 == P(cast(ByInt)3, StringEnum.two)); 1351 1352 1353 assert(B().serializeJson 1354 == `{"builtin":"one","bstr":"one"}`); 1355 assert(P().serializeJson 1356 == `{"byInt":1,"str":"eins"}`); 1357 1358 // serializing invalid enum values may cause AssertError or other errors, 1359 // so avoid doing it altogether. 1360 assertThrown!Throwable(B(cast(Builtin)2, cast(BuiltinString)"drei").serializeJson); 1361 assertThrown!Throwable(B(cast(Builtin)3, BuiltinString.two).serializeJson); 1362 // Fine with @serdeProxy!T with serialization, although inconsistent. 1363 // Use @serdeEnumProxy!T or add @serdeProxyCast to your serdeProxy annotated value 1364 // to also support serialization and deserialization through the underlying type. 1365 assert(P(cast(ByInt)2, cast(StringEnum)"drei").serializeJson 1366 == `{"byInt":2,"str":"StringEnum(drei)"}`); 1367 assert(P(cast(ByInt)3, StringEnum.two).serializeJson 1368 == `{"byInt":3,"str":"zwei"}`); 1369 } 1370 1371 /// ditto 1372 version(mir_ion_test) unittest 1373 { 1374 import mir.serde: serdeEnumProxy; 1375 1376 @serdeEnumProxy!int 1377 enum IntE 1378 { 1379 one = 1, 1380 two = 2 1381 } 1382 1383 @serdeEnumProxy!string 1384 enum StrE : string 1385 { 1386 one = "eins", 1387 two = "zwei" 1388 } 1389 1390 static struct S 1391 { 1392 IntE a; 1393 StrE b; 1394 } 1395 1396 import mir.ser.json : serializeJson; 1397 import mir.deser.json : deserializeJson; 1398 1399 // same as regular serdeProxy so far 1400 assert(`{"a":1,"b":"eins"}`.deserializeJson!S == S(IntE.one, StrE.one)); 1401 assert(`{"a":2,"b":"zwei"}`.deserializeJson!S == S(IntE.two, StrE.two)); 1402 1403 assert(`{"a":1,"b":"eins"}` == S(IntE.one, StrE.one).serializeJson); 1404 assert(`{"a":2,"b":"zwei"}` == S(IntE.two, StrE.two).serializeJson); 1405 1406 // but allows any underlying values 1407 assert(`{"a":3,"b":"drei"}`.deserializeJson!S 1408 == S(cast(IntE)3, cast(StrE)"drei")); 1409 assert(`{"a":3,"b":"drei"}` 1410 == S(cast(IntE)3, cast(StrE)"drei").serializeJson, 1411 S(cast(IntE)3, cast(StrE)"drei").serializeJson); 1412 } 1413 1414 version(mir_ion_test) unittest 1415 { 1416 import mir.serde: serdeEnumProxy; 1417 import mir.algebraic: Nullable, Variant, nullable; 1418 1419 @serdeEnumProxy!string 1420 enum StrE : string 1421 { 1422 one = "eins", 1423 two = "zwei" 1424 } 1425 1426 static struct S 1427 { 1428 Nullable!(StrE[]) a; 1429 Variant!(void, StrE) b; 1430 } 1431 1432 import mir.ser.json : serializeJson; 1433 import mir.deser.json : deserializeJson; 1434 1435 auto s = S([StrE.one, StrE.two, cast(StrE)"drei"].nullable); 1436 s.b = StrE.two; 1437 auto str = `{"a":["eins","zwei","drei"],"b":"zwei"}`; 1438 1439 assert(str.deserializeJson!S == s); 1440 assert(s.serializeJson == str, str); 1441 } 1442 1443 // check symbols support 1444 // check typed nullable support 1445 // check void support 1446 version(mir_ion_test) unittest 1447 { 1448 import mir.algebraic_alias.ion; 1449 import mir.algebraic; 1450 import mir.annotated; 1451 import mir.deser.text; 1452 import mir.ser.text; 1453 import mir.ion.type_code; 1454 import mir.ion.value: IonNull; 1455 import mir.small_string; 1456 import mir.timestamp; 1457 assert(`aSymbol`.deserializeText!(Nullable!(bool, double, long, string, Timestamp)) == `aSymbol`); 1458 assert(`aSymbol`.deserializeText!(Nullable!(bool, double, long, SmallString!32, Timestamp)) == `aSymbol`.SmallString!32); 1459 assert(`null.timestamp`.deserializeText!(Nullable!Timestamp) == null); 1460 assert(`null.timestamp`.deserializeText!(Nullable!(bool, Timestamp)) == null); 1461 //IonNull support 1462 assert(`null.timestamp`.deserializeText!(Variant!(IonNull, bool, Timestamp)) == IonTypeCode.timestamp.IonNull); 1463 //Annotated support 1464 auto annotated = Annotated!IonAlgebraic(["birthday"], Timestamp("2001-01-01")); 1465 auto variant = `birthday::2001-01-01`.deserializeText!IonAlgebraic; 1466 // assert(variant == annotated); 1467 assert(Nullable!(void, int)(3).serializeText == "3"); 1468 } 1469 1470 version(mir_ion_test) unittest 1471 { 1472 import mir.deser.text; 1473 import mir.ser.text; 1474 enum CaseSensitive 1475 { 1476 no, 1477 yes, 1478 osDefault = yes, 1479 } 1480 1481 assert(CaseSensitive.osDefault.serializeText == `yes`); 1482 assert(`yes`.deserializeText!CaseSensitive == CaseSensitive.yes); 1483 1484 enum TextAlignment : uint { 1485 Left = 0, /// 1486 Center = 1, /// 1487 Right = 2, /// 1488 1489 VerticalTop = 0, /// 1490 VerticalCenter = 4, /// 1491 VerticalBottom = 8, /// 1492 } 1493 1494 assert(TextAlignment.VerticalTop.serializeText == `Left`); 1495 assert(`Left`.deserializeText!TextAlignment == TextAlignment.Left); 1496 } 1497 1498 version(mir_ion_test) unittest 1499 { 1500 import mir.ndslice.topology: iota, as; 1501 auto range = as!char(3.iota + 'a'); 1502 assert(range == "abc"); 1503 import mir.ser.text; 1504 assert(range.serializeText == `"abc"`); 1505 } 1506 1507 /// Series de/serialization 1508 @safe version(mir_ion_test) unittest 1509 { 1510 import mir.test; 1511 import mir.deser.text; 1512 import mir.ser.text; 1513 import mir.series; 1514 auto s = ["a", "b"].series([5, 6]); 1515 auto t = `{index:["a","b"],data:[5,6]}`; 1516 auto r = `{index:["b","a"],data:[6,5]}`; 1517 s.serializeText.should == t; 1518 assert(r.deserializeText!(typeof(s)) == s, r.deserializeText!(typeof(s)).serializeText); 1519 } 1520 1521 // Const series de/serialization 1522 @safe version(mir_ion_test) unittest 1523 { 1524 import mir.deser.text; 1525 import mir.ser.text; 1526 import mir.series; 1527 auto s = ["a", "b"].series([5, 6]).lightConst; 1528 auto t = `{index:["a","b"],data:[5,6]}`; 1529 auto r = `{index:["b","a"],data:[6,5]}`; 1530 assert(s.serializeText == t); 1531 assert(r.deserializeText!(typeof(s)) == s, r.deserializeText!(typeof(s)).serializeText); 1532 } 1533 1534 /// RC Series de/serialization 1535 @safe version(mir_ion_test) unittest 1536 { 1537 import mir.test; 1538 import mir.deser.text; 1539 import mir.ser.text; 1540 import mir.ndslice.allocation: rcslice; 1541 import mir.series; 1542 import mir.small_string; 1543 auto s = [SmallString!32("a"), SmallString!32("b")].rcslice.series([5, 6].rcslice); 1544 auto t = `{index:["a","b"],data:[5,6]}`; 1545 auto r = `{index:["b","a"],data:[6,5]}`; 1546 assert(s.serializeText == t); 1547 assert(r.deserializeText!(typeof(s)) == s); 1548 } 1549 1550 // Const RC Series de/serialization 1551 @safe version(mir_ion_test) unittest 1552 { 1553 import mir.deser.text; 1554 import mir.ser.text; 1555 import mir.ndslice.allocation: rcslice; 1556 import mir.series; 1557 import mir.small_string; 1558 auto s = [SmallString!32("a"), SmallString!32("b")].rcslice.series([5, 6].rcslice).lightConst; 1559 auto t = `{index:["a","b"],data:[5,6]}`; 1560 auto r = `{index:["b","a"],data:[6,5]}`; 1561 assert(s.serializeText == t); 1562 assert(r.deserializeText!(typeof(s)) == s, r.deserializeText!(typeof(s)).serializeText); 1563 } 1564 1565 version(mir_ion_test) 1566 unittest 1567 { 1568 import std.exception: assertThrown; 1569 import mir.deser.json; 1570 static struct S {} 1571 ",".deserializeJson!S.assertThrown!Exception; 1572 } 1573 1574 /// 1575 @safe pure version(mir_ion_test) unittest 1576 { 1577 import mir.algebraic: Nullable; 1578 import mir.ser.text: serializeText; 1579 import mir.deser.text: deserializeText; 1580 import mir.serde: serdeDiscriminatedField; 1581 1582 @serdeDiscriminatedField("tag", "data") 1583 static struct D 1584 { 1585 int num; 1586 } 1587 1588 @serdeDiscriminatedField("tag", "gr") 1589 static struct G 1590 { 1591 string val; 1592 } 1593 1594 alias V = Nullable!(string, D, G); 1595 1596 assert(D(123).serializeText == `{tag:data,num:123}`); 1597 assert(V(D(123)).serializeText == `{tag:data,num:123}`); 1598 assert(`{tag:data,num:123}`.deserializeText!V == D(123)); 1599 assert(`{val:"str",tag:gr}`.deserializeText!V == G("str")); 1600 } 1601 1602 1603 /// 1604 version(mir_ion_test) 1605 unittest 1606 { 1607 import mir.ser.text; 1608 import mir.deser.text; 1609 1610 @serdeProxy!int 1611 enum Foo 1612 { 1613 a = 1, 1614 b = 2 1615 } 1616 1617 assert((cast(Foo)6).serializeText == "6", Foo.b.serializeText); 1618 assert("6".deserializeText!Foo == 6); 1619 } 1620 1621 /// @serdeOrderedIn support 1622 version(mir_ion_test) 1623 @safe pure 1624 unittest 1625 { 1626 import mir.deser.text; 1627 import mir.serde: serdeOrderedIn; 1628 import mir.test: should; 1629 1630 @serdeOrderedIn 1631 static struct Foo 1632 { 1633 int a; 1634 private int b_; 1635 1636 auto b(int value) @property 1637 { 1638 b_ = value; 1639 // a is always set here 1640 assert(a + b_ == 7); 1641 } 1642 } 1643 1644 `{b:3,a:4}`.deserializeText!Foo.should == Foo(4, 3); 1645 } 1646 1647 /// `new C` support for classes 1648 version(mir_ion_test) 1649 @safe pure 1650 unittest 1651 { 1652 import mir.deser.text; 1653 import mir.test: should; 1654 1655 static class C 1656 { 1657 int a; 1658 this() @safe pure { 1659 } 1660 } 1661 1662 `{a:4}`.deserializeText!C.a.should == 4; 1663 } 1664 1665 /// Tuple support 1666 unittest 1667 { 1668 import mir.test: should; 1669 import mir.functional: Tuple; 1670 import mir.ser.text: serializeText; 1671 import mir.deser.text: deserializeText; 1672 1673 alias T = Tuple!(uint, string, double[]); 1674 auto value = T(3, "str", [3.4, 23]); 1675 auto text = `[3,"str",[3.4,23.0]]`; 1676 value.serializeText.should == text; 1677 text.deserializeText!T.should == value; 1678 1679 // combination with Algebraic 1680 import mir.algebraic: Variant; 1681 alias V = Variant!(T, T[string], T); 1682 1683 text.deserializeText!V.should == value; 1684 `{key: [3,"str",[3.4,23.0]]}` 1685 .deserializeText!V 1686 .get!(T[string])["key"] 1687 .should == value; 1688 } 1689 1690 version(mir_ion_test) 1691 @safe pure 1692 unittest 1693 { 1694 import mir.algebraic : Variant; 1695 import mir.deser.text; 1696 import mir.ser.text; 1697 import mir.serde; 1698 import mir.test; 1699 1700 enum SerialContract {serial, contract} 1701 1702 @serdeAnnotation 1703 @serdeAlgebraicAnnotation("stirFuture") 1704 static struct StirFutureConfig 1705 { 1706 @serdeAnnotation 1707 SerialContract kind; 1708 1709 int data; 1710 alias data this; 1711 } 1712 1713 auto config = Variant!StirFutureConfig(SerialContract.contract, 7); 1714 auto str = "stirFuture::contract::7"; 1715 config.serializeText.should == "stirFuture::contract::7"; 1716 `stirFuture::contract::7` 1717 .deserializeText!(Variant!StirFutureConfig).should == config; 1718 } 1719 1720 version(mir_ion_test) 1721 @safe pure 1722 unittest 1723 { 1724 import mir.deser.text; 1725 import mir.serde; 1726 @serdeIgnoreCase enum E { a, b } 1727 assert (`b`.deserializeText!E == E.b); 1728 assert (`B`.deserializeText!E == E.b); 1729 } 1730 1731 version(mir_ion_test) 1732 @safe pure 1733 unittest 1734 { 1735 import std.exception: assertThrown; 1736 import mir.ion.conv; 1737 "\"".json2ion.assertThrown; 1738 "??:? [0x55d24c502acd]".json2ion.assertThrown; 1739 "??:? [0x55d24c502acd]\"".json2ion.assertThrown; 1740 "??:? [0x55d24c502acd]\"}".json2ion.assertThrown; 1741 }