1 module msgpack.value; 2 3 import msgpack.common; 4 import msgpack.attribute; 5 import msgpack.exception; 6 7 import std.json; 8 import std.container : Array; 9 import std.traits; 10 import std.typecons : Tuple, isTuple; 11 12 13 /** 14 * $(D Value) is a $(D MessagePack) value representation 15 * 16 * Example: 17 * ----- 18 * auto unpacker = StreamingUnpacker(pack(1, 0.1L) ~ pack(true) ~ pack("foobarbaz")); 19 * 20 * foreach (unpacked; unpacker) { 21 * if (unpacked.type == Value.Type.array) { 22 * foreach (obj; unpacked) { 23 * switch (obj.type) { 24 * case Value.Type.unsigned: writeln(obj.as!(uint)); break; 25 * case Value.Type.floating: writeln(obj.as!(real)); break; 26 * defalut: 27 * throw new Exception("Unknown type"); 28 * } 29 * } 30 * } else { 31 * if (unpacked.type == Value.Type.boolean) 32 * writeln(unpacked.as!(bool)); 33 * else 34 * writeln("Message: ", unpacked.as!(string)); 35 * } 36 * } 37 * ----- 38 */ 39 struct Value 40 { 41 /** 42 * $(D MessagePack) value type 43 */ 44 static enum Type 45 { 46 nil, /// nil(null in D) 47 boolean, /// true, false 48 unsigned, /// positive fixnum, uint 8, uint 16, uint 32, uint 64 49 signed, /// negative fixnum, int 8, int 16, int 32, int 64 50 floating, /// float, double, real 51 array, /// fix array, array 16, array 32 52 map, /// fix map, map 16, map 32 53 raw, /// fix raw, raw 16, raw 32 54 ext /// fix ext, ext8, ext16, ext32 55 } 56 57 58 /** 59 * msgpack value representation 60 */ 61 static union Via 62 { 63 bool boolean; /// corresponding to Type.boolean 64 ulong uinteger; /// corresponding to Type.unsigned 65 long integer; /// corresponding to Type.signed 66 real floating; /// corresponding to Type.floating 67 Value[] array; /// corresponding to Type.array 68 Value[Value] map; /// corresponding to Type.map 69 ubyte[] raw; /// corresponding to Type.raw 70 ExtValue ext; /// corresponding to Type.ext 71 } 72 73 74 Type type; /// represents value type 75 Via via; /// represents real value 76 77 78 /** 79 * Constructs a $(D Value) with arguments. 80 * 81 * Params: 82 * value = the real content. 83 * type = the type of value. 84 */ 85 @safe 86 this(Type type) 87 { 88 this.type = type; 89 } 90 91 @safe 92 this(typeof(null)) 93 { 94 this(Type.nil); 95 } 96 97 /// ditto 98 @trusted 99 this(bool value, Type type = Type.boolean) 100 { 101 this(type); 102 via.boolean = value; 103 } 104 105 106 /// ditto 107 @trusted 108 this(ulong value, Type type = Type.unsigned) 109 { 110 this(type); 111 via.uinteger = value; 112 } 113 114 115 /// ditto 116 @trusted 117 this(long value, Type type = Type.signed) 118 { 119 this(type); 120 via.integer = value; 121 } 122 123 124 /// ditto 125 @trusted 126 this(real value, Type type = Type.floating) 127 { 128 this(type); 129 via.floating = value; 130 } 131 132 133 /// ditto 134 @trusted 135 this(Value[] value, Type type = Type.array) 136 { 137 this(type); 138 via.array = value; 139 } 140 141 142 /// ditto 143 @trusted 144 this(Value[Value] value, Type type = Type.map) 145 { 146 this(type); 147 via.map = value; 148 } 149 150 151 /// ditto 152 @trusted 153 this(ubyte[] value, Type type = Type.raw) 154 { 155 this(type); 156 via.raw = value; 157 } 158 159 /// This is unsafe overload because using cast internally. 160 @trusted 161 this(string value, Type type = Type.raw) 162 { 163 this(type); 164 via.raw = cast(ubyte[])value; 165 } 166 167 /** 168 * Constructs a $(D Value) with arguments. 169 * 170 * Params: 171 * value = the real content. 172 * type = the type of value. 173 */ 174 @trusted 175 this(ExtValue value, Type type = Type.ext) 176 { 177 this(type); 178 via.ext = value; 179 } 180 181 /** 182 * Converts value to $(D_PARAM T) type. 183 * 184 * Returns: 185 * converted value. 186 * 187 * Throws: 188 * MessagePackException if type is mismatched. 189 * 190 * NOTE: 191 * Current implementation uses cast. 192 */ 193 @property @trusted 194 T as(T)() if (is(Unqual!T == bool)) 195 { 196 if (type != Type.boolean) 197 onCastError(); 198 199 return via.boolean; 200 } 201 202 203 /// ditto 204 @property @trusted 205 T as(T)() if (isIntegral!T && !is(Unqual!T == enum)) 206 { 207 if (type == Type.unsigned) 208 return cast(T)via.uinteger; 209 210 if (type == Type.signed) 211 return cast(T)via.integer; 212 213 onCastError(); 214 215 assert(false); 216 } 217 218 219 /// ditto 220 @property @trusted 221 T as(T)() if (isFloatingPoint!T && !is(Unqual!T == enum)) 222 { 223 if (type != Type.floating) 224 onCastError(); 225 226 return cast(T)via.floating; 227 } 228 229 230 /// ditto 231 @property @trusted 232 T as(T)() if (is(Unqual!T == enum)) 233 { 234 return cast(T)as!(OriginalType!T); 235 } 236 237 238 /// ditto 239 @property @trusted 240 T as(T)() if (is(Unqual!T == ExtValue)) 241 { 242 if (type != Type.ext) 243 onCastError(); 244 245 return cast(T)via.ext; 246 } 247 248 249 /// ditto 250 @property @trusted 251 T as(T)() if ((isArray!T || 252 isInstanceOf!(Array, T)) && 253 !is(Unqual!T == enum)) 254 { 255 alias typeof(T.init[0]) V; 256 257 if (type == Type.nil) { 258 static if (isDynamicArray!T) { 259 return null; 260 } else { 261 return T.init; 262 } 263 } 264 265 static if (isByte!V || isSomeChar!V) { 266 if (type != Type.raw) 267 onCastError(); 268 269 static if (isDynamicArray!T) { 270 return cast(T)via.raw; 271 } else { 272 if (via.raw.length != T.length) 273 onCastError(); 274 275 return cast(T)(via.raw[0 .. T.length]); 276 } 277 } else { 278 if (type != Type.array) 279 onCastError(); 280 281 V[] array; 282 283 foreach (elem; via.array) 284 array ~= elem.as!(V); 285 286 return array; 287 } 288 } 289 290 291 /// ditto 292 @property @trusted 293 T as(T)() if (isAssociativeArray!T) 294 { 295 alias typeof(T.init.keys[0]) K; 296 alias typeof(T.init.values[0]) V; 297 298 if (type == Type.nil) 299 return null; 300 301 if (type != Type.map) 302 onCastError(); 303 304 V[K] map; 305 306 foreach (key, value; via.map) 307 map[key.as!(K)] = value.as!(V); 308 309 return map; 310 } 311 312 313 /** 314 * Converts to $(D_PARAM T) type. 315 * 316 * Calling $(D fromMsgpack) if $(D_KEYWORD class) and $(D_KEYWORD struct) implement $(D fromMsgpack) method. $(D fromMsgpack) signature is: 317 * ----- 318 * void fromMsgpack(Value value) 319 * ----- 320 * This method assigns converted values to all members of T object if $(D_KEYWORD class) and $(D_KEYWORD struct) don't implement $(D fromMsgpack). 321 * 322 * Params: 323 * args = arguments to class constructor(class only). 324 * 325 * Returns: 326 * converted value. 327 */ 328 @property @trusted 329 T as(T, Args...)(Args args) if (is(Unqual!T == class)) 330 { 331 if (type == Type.nil) 332 return null; 333 334 T object = new T(args); 335 336 static if (hasMember!(T, "fromMsgpack")) 337 { 338 static if (__traits(compiles, { object.fromMsgpack(this); })) { 339 object.fromMsgpack(this); 340 } else { 341 static assert(0, "Failed to invoke 'fromMsgpack' on type '" ~ Unqual!T.stringof ~ "'"); 342 } 343 } else { 344 alias SerializingClasses!(T) Classes; 345 346 if (via.array.length != SerializingMemberNumbers!(Classes)) 347 throw new MessagePackException("The number of deserialized object member is mismatched"); 348 349 size_t offset; 350 foreach (Class; Classes) { 351 Class obj = cast(Class)object; 352 foreach (i, member; obj.tupleof) { 353 static if (isPackedField!(Class.tupleof[i])) 354 obj.tupleof[i] = via.array[offset++].as!(typeof(member)); 355 } 356 } 357 } 358 359 return object; 360 } 361 362 363 /// ditto 364 @property @trusted 365 T as(T)() if (is(Unqual!T == struct) && !is(Unqual!T == ExtValue)) 366 { 367 T obj; 368 369 static if (hasMember!(T, "fromMsgpack")) 370 { 371 static if (__traits(compiles, { obj.fromMsgpack(this); })) { 372 obj.fromMsgpack(this); 373 } else { 374 static assert(0, "Failed to invoke 'fromMsgpack' on type '" ~ Unqual!T.stringof ~ "'"); 375 } 376 } else { 377 static if (isTuple!T) { 378 if (via.array.length != T.Types.length) 379 throw new MessagePackException("The number of deserialized Tuple element is mismatched"); 380 381 foreach (i, Type; T.Types) 382 obj.field[i] = via.array[i].as!(Type); 383 } else { // simple struct 384 if (via.array.length != SerializingMemberNumbers!T) 385 throw new MessagePackException("The number of deserialized struct member is mismatched"); 386 387 size_t offset; 388 foreach (i, member; obj.tupleof) { 389 static if (isPackedField!(T.tupleof[i])) 390 obj.tupleof[i] = via.array[offset++].as!(typeof(member)); 391 } 392 } 393 } 394 395 return obj; 396 } 397 398 399 /** 400 * Special method called by $(D Packer). 401 * 402 * Params: 403 * packer = a MessagePack serializer. 404 */ 405 void toMsgpack(Packer)(ref Packer packer) const 406 { 407 final switch (type) { 408 case Type.nil: 409 packer.pack(null); 410 break; 411 case Type.boolean: 412 packer.pack(via.boolean); 413 break; 414 case Type.unsigned: 415 packer.pack(via.uinteger); 416 break; 417 case Type.signed: 418 packer.pack(via.integer); 419 break; 420 case Type.floating: 421 packer.pack(via.floating); 422 break; 423 case Type.raw: 424 packer.pack(via.raw); 425 break; 426 case Type.ext: 427 packer.packExt(via.ext.type, via.ext.data); 428 break; 429 case Type.array: 430 packer.beginArray(via.array.length); 431 foreach (elem; via.array) 432 elem.toMsgpack(packer); 433 break; 434 case Type.map: 435 packer.beginMap(via.map.length); 436 foreach (key, value; via.map) { 437 key.toMsgpack(packer); 438 value.toMsgpack(packer); 439 } 440 break; 441 } 442 } 443 444 445 /** 446 * Comparison for equality. @trusted for union. 447 */ 448 @trusted 449 bool opEquals(Tdummy = void)(ref const Value other) const 450 { 451 if (type != other.type) 452 return false; 453 454 final switch (other.type) { 455 case Type.nil: return true; 456 case Type.boolean: return opEquals(other.via.boolean); 457 case Type.unsigned: return opEquals(other.via.uinteger); 458 case Type.signed: return opEquals(other.via.integer); 459 case Type.floating: return opEquals(other.via.floating); 460 case Type.raw: return opEquals(other.via.raw); 461 case Type.ext: return opEquals(other.via.ext); 462 case Type.array: return opEquals(other.via.array); 463 case Type.map: return opEquals(other.via.map); 464 } 465 } 466 467 468 /// ditto 469 @trusted 470 bool opEquals(T : bool)(in T other) const 471 { 472 if (type != Type.boolean) 473 return false; 474 475 return via.boolean == other; 476 } 477 478 479 /// ditto 480 @trusted 481 bool opEquals(T : ulong)(in T other) const 482 { 483 static if (__traits(isUnsigned, T)) { 484 if (type != Type.unsigned) 485 return false; 486 487 return via.uinteger == other; 488 } else { 489 if (type != Type.signed) 490 return false; 491 492 return via.integer == other; 493 } 494 } 495 496 497 /// ditto 498 @trusted 499 bool opEquals(T : real)(in T other) const 500 { 501 if (type != Type.floating) 502 return false; 503 504 return via.floating == other; 505 } 506 507 508 /// ditto 509 @trusted 510 bool opEquals(T : const Value[])(in T other) const 511 { 512 if (type != Type.array) 513 return false; 514 515 return via.array == other; 516 } 517 518 519 /// ditto 520 @trusted 521 bool opEquals(T : const Value[Value])(in T other) const 522 { 523 if (type != Type.map) 524 return false; 525 526 // This comparison is instead of default comparison because 'via.map == other' raises "Access Violation". 527 foreach (key, value; via.map) { 528 if (key in other) { 529 if (other[key] != value) 530 return false; 531 } else { 532 return false; 533 } 534 } 535 536 return true; 537 } 538 539 540 /// ditto 541 @trusted 542 bool opEquals(T : const(ubyte)[])(in T other) const 543 { 544 if (type != Type.raw) 545 return false; 546 547 return via.raw == other; 548 } 549 550 551 /// ditto 552 @trusted 553 bool opEquals(T : string)(in T other) const 554 { 555 if (type != Type.raw) 556 return false; 557 558 return via.raw == cast(ubyte[])other; 559 } 560 561 562 // 563 @trusted 564 bool opEquals(T : ExtValue)(in T other) const 565 { 566 if (type != Type.ext) 567 return false; 568 569 return via.ext.type == other.type && via.ext.data == other.data; 570 } 571 572 573 @trusted 574 hash_t toHash() const nothrow 575 { 576 static hash_t getHash(T)(T* v) @safe nothrow 577 { 578 return typeid(T).getHash(v); 579 } 580 581 final switch (type) { 582 case Type.nil: return 0; 583 case Type.boolean: return getHash(&via.boolean); 584 case Type.unsigned: return getHash(&via.uinteger); 585 case Type.signed: return getHash(&via.integer); 586 case Type.floating: return getHash(&via.floating); 587 case Type.raw: return getHash(&via.raw); 588 case Type.ext: return getHash(&via.ext); 589 case Type.array: 590 hash_t ret; 591 foreach (elem; via.array) 592 ret ^= elem.toHash(); 593 return ret; 594 case Type.map: 595 try { 596 hash_t ret; 597 foreach (key, value; via.map) { 598 ret ^= key.toHash(); 599 ret ^= value.toHash(); 600 } 601 return ret; 602 } catch(Throwable) assert(0); 603 } 604 } 605 } 606 607 608 unittest 609 { 610 import std.array; 611 612 // nil 613 Value value = Value(null); 614 Value other = Value(); 615 616 assert(value == other); 617 assert(value.type == Value.Type.nil); 618 619 // boolean 620 value = Value(true); 621 other = Value(false); 622 623 assert(value != other); 624 assert(value.type == Value.Type.boolean); 625 assert(value.as!(bool) == true); 626 assert(other == false); 627 628 try { 629 auto b = value.as!(uint); 630 assert(false); 631 } catch (MessagePackException e) { } 632 633 // unsigned integer 634 value = Value(10UL); 635 other = Value(10UL); 636 637 assert(value == other); 638 assert(value.type == Value.Type.unsigned); 639 assert(value.as!(uint) == 10); 640 assert(other == 10UL); 641 642 // signed integer 643 value = Value(-20L); 644 other = Value(-10L); 645 646 assert(value != other); 647 assert(value.type == Value.Type.signed); 648 assert(value.as!(int) == -20); 649 assert(other == -10L); 650 651 // enum 652 enum E : int { F = -20 } 653 654 E e = value.as!(E); 655 assert(e == E.F); 656 657 // floating point 658 value = Value(0.1e-10L); 659 other = Value(0.1e-20L); 660 661 assert(value != other); 662 assert(value.type == Value.Type.floating); 663 assert(value.as!(real) == 0.1e-10L); 664 assert(other == 0.1e-20L); 665 666 // raw 667 value = Value(cast(ubyte[])[72, 105, 33]); 668 other = Value(cast(ubyte[])[72, 105, 33]); 669 670 assert(value == other); 671 assert(value.type == Value.Type.raw); 672 assert(value.as!(string) == "Hi!"); 673 assert(value.as!(ubyte[3]) == [72, 105, 33]); 674 assert(other == cast(ubyte[])[72, 105, 33]); 675 676 // raw with string 677 value = Value("hello"); 678 other = Value("hello"); 679 680 assert(value == other); 681 assert(value.type == Value.Type.raw); 682 assert(value.as!(string) == "hello"); 683 684 // enum : string 685 enum EStr : string { elem = "hello" } 686 687 assert(value.as!(EStr) == EStr.elem); 688 689 // ext 690 auto ext = ExtValue(7, [1,2,3]); 691 value = Value(ExtValue(7, [1,2,3])); 692 assert(value.as!ExtValue == ext); 693 694 // array 695 auto t = Value(cast(ubyte[])[72, 105, 33]); 696 value = Value([t]); 697 other = Value([t]); 698 699 assert(value == other); 700 assert(value.type == Value.Type.array); 701 assert(value.as!(string[]) == ["Hi!"]); 702 assert(other == [t]); 703 704 // map 705 value = Value([Value(1L):Value(2L)]); 706 other = Value([Value(1L):Value(1L)]); 707 708 assert(value != other); 709 assert(value.type == Value.Type.map); 710 assert(value.as!(int[int]) == [1:2]); 711 assert(other == [Value(1L):Value(1L)]); 712 713 value = Value(10UL); 714 715 // struct 716 static struct S 717 { 718 ulong num; 719 720 void fromMsgpack(Value value) { num = value.via.uinteger; } 721 } 722 723 S s = value.as!(S); 724 assert(s.num == 10); 725 726 value = Value([Value(0.5f), Value(cast(ubyte[])[72, 105, 33])]); 727 728 // struct 729 static struct Simple 730 { 731 @nonPacked int era; 732 double num; 733 string msg; 734 } 735 736 Simple simple = value.as!(Simple); 737 assert(simple.era == int.init); 738 assert(simple.num == 0.5f); 739 assert(simple.msg == "Hi!"); 740 741 value = Value(10UL); 742 743 // class 744 static class C 745 { 746 ulong num; 747 748 void fromMsgpack(Value value) { num = value.via.uinteger; } 749 } 750 751 C c = value.as!(C); 752 assert(c.num == 10); 753 754 static class SimpleA 755 { 756 bool flag = true; 757 } 758 759 static class SimpleB : SimpleA 760 { 761 ubyte type = 100; 762 } 763 764 static class SimpleC : SimpleB 765 { 766 @nonPacked string str; 767 uint num = uint.max; 768 } 769 770 value = Value([Value(false), Value(99UL), Value(cast(ulong)(uint.max / 2u))]); 771 772 SimpleC sc = value.as!(SimpleC); 773 assert(sc.flag == false); 774 assert(sc.type == 99); 775 assert(sc.num == uint.max / 2); 776 assert(sc.str.empty); 777 778 // std.typecons.Tuple 779 value = Value([Value(true), Value(1UL), Value(cast(ubyte[])"Hi!")]); 780 781 auto tuple = value.as!(Tuple!(bool, uint, string)); 782 assert(tuple.field[0] == true); 783 assert(tuple.field[1] == 1u); 784 assert(tuple.field[2] == "Hi!"); 785 786 /* 787 * non-MessagePackable object is stopped by static assert 788 * static struct NonMessagePackable {} 789 * auto nonMessagePackable = value.as!(NonMessagePackable); 790 */ 791 } 792 793 794 /** 795 * Converts $(D Value) to $(D JSONValue). 796 * 797 * Params: 798 * val = $(D Value) to convert. 799 * 800 * Returns: 801 * a $(D JSONValue). 802 */ 803 @trusted 804 JSONValue toJSONValue(in Value val) 805 { 806 final switch (val.type) 807 { 808 case Value.Type.nil: return JSONValue(null); 809 case Value.Type.boolean: return JSONValue(val.via.boolean); 810 case Value.Type.unsigned: return JSONValue(val.via.uinteger); 811 case Value.Type.signed: return JSONValue(val.via.integer); 812 case Value.Type.floating: return JSONValue(val.via.floating); 813 case Value.Type.raw: return JSONValue(cast(string)(val.via.raw.idup)); 814 case Value.Type.ext: throw new MessagePackException("Unable to convert ext to json"); 815 case Value.Type.array: { 816 JSONValue[] vals; 817 foreach (elem; val.via.array) 818 vals ~= elem.toJSONValue(); 819 return JSONValue(vals); 820 } 821 case Value.Type.map: { 822 JSONValue[string] vals; 823 foreach (key, value; val.via.map) { 824 if (key.type != Value.Type.raw) 825 { 826 throw new MessagePackException("JSON-object key must be a raw type"); 827 } 828 vals[key.as!string] = value.toJSONValue(); 829 } 830 return JSONValue(vals); 831 } 832 } 833 } 834 835 /** 836 * Converts $(D JSONValue) to $(D Value). 837 * 838 * Params: 839 * val = $(D JSONValue) to convert. 840 * 841 * Returns: 842 * a $(D Value). 843 */ 844 @trusted 845 Value fromJSONValue(in JSONValue val) 846 { 847 final switch (val.type()) 848 { 849 case JSONType.null_: return Value(null); 850 case JSONType.true_: return Value(true); 851 case JSONType.false_: return Value(false); 852 case JSONType.uinteger: return Value(val.uinteger); 853 case JSONType.integer: return Value(val.integer); 854 case JSONType.float_: return Value(val.floating); 855 case JSONType..string: return Value(cast(ubyte[])(val.str)); 856 case JSONType.array: { 857 Value[] vals; 858 foreach (elem; val.array) 859 vals ~= elem.fromJSONValue(); 860 return Value(vals); 861 } 862 case JSONType.object: { 863 Value[Value] vals; 864 foreach (key, value; val.object) { 865 vals[Value(cast(ubyte[])key)] = value.fromJSONValue(); 866 } 867 return Value(vals); 868 } 869 } 870 } 871 872 unittest 873 { 874 import std.array : array; 875 import std.algorithm : equal, map; 876 import std.conv; 877 import std.math : isClose; 878 import std.range; 879 import msgpack; 880 881 // nil 882 Value value = Value(null); 883 884 assert(toJSONValue(value).type() == JSONType.null_); 885 886 // boolean 887 value = Value(true); 888 auto other = Value(false); 889 890 assert(toJSONValue(value).type() == JSONType.true_); 891 assert(toJSONValue(other).type() == JSONType.false_); 892 893 // unsigned integer 894 value = Value(10UL); 895 896 assert(value.toJSONValue().type == JSONType.uinteger); 897 assert(value.toJSONValue().uinteger == value.as!uint); 898 assert(value.toJSONValue().uinteger == 10UL); 899 900 // signed integer 901 value = Value(-20L); 902 903 assert(value.toJSONValue().type == JSONType.integer); 904 assert(value.toJSONValue().integer == value.as!int); 905 906 // enum 907 enum E : int { F = -20 } 908 value = Value(cast(long)(E.F)); 909 910 assert(value.toJSONValue().type == JSONType.integer); 911 assert(value.toJSONValue().integer == E.F); 912 913 // floating point 914 value = Value(0.1e-10L); 915 other = Value(0.1e-20L); 916 917 assert(value.toJSONValue().type == JSONType.float_); 918 assert(other.toJSONValue().type == JSONType.float_); 919 920 assert(isClose(value.toJSONValue().floating, 0.1e-10L)); 921 assert(isClose(other.toJSONValue().floating, 0.1e-20L)); 922 923 // raw 924 long[] arr = [72, 105, 33]; 925 value = Value(to!(ubyte[])(arr)); 926 927 assert(value.toJSONValue().type == JSONType..string); 928 assert(equal(value.toJSONValue().str, arr)); 929 930 // raw with string 931 value = Value("hello"); 932 assert(value.toJSONValue().type == JSONType..string); 933 assert(value.toJSONValue().str == "hello"); 934 935 // array 936 auto t = Value(to!(ubyte[])(arr)); 937 value = Value([t]); 938 other = Value(array(map!(a => Value(a))(arr))); 939 940 assert(value.toJSONValue().type == JSONType.array); 941 assert(value.toJSONValue().array.length == 1); 942 assert(value.toJSONValue().array.front().type == JSONType..string); 943 assert(equal(value.toJSONValue().array.front().str, arr)); 944 assert(other.toJSONValue().type == JSONType.array); 945 assert(array(map!(a => a.integer)(other.toJSONValue().array)) == arr); 946 947 // map 948 value = Value([Value("key"):Value(2L)]); 949 950 assert(value.toJSONValue().type == JSONType.object); 951 assert("key" in value.toJSONValue().object); 952 assert(value.toJSONValue().object["key"].type == JSONType.integer); 953 assert(value.toJSONValue().object["key"].integer == 2L); 954 955 // struct 956 static struct Simple 957 { 958 @nonPacked int era; 959 double num; 960 string msg; 961 } 962 963 Simple simple; 964 simple.era = 5; 965 simple.num = 13.5; 966 simple.msg = "helloworld"; 967 value = simple.pack().unpack().value; 968 969 assert(value.toJSONValue().type == JSONType.array); 970 assert(value.toJSONValue().array.length == 2); 971 assert(value.toJSONValue().array[0].type == JSONType.float_); 972 assert(isClose(value.toJSONValue().array[0].floating, simple.num)); 973 assert(value.toJSONValue().array[1].type == JSONType..string); 974 assert(value.toJSONValue().array[1].str == simple.msg); 975 976 // class 977 static class SimpleA 978 { 979 bool flag = true; 980 } 981 982 static class SimpleB : SimpleA 983 { 984 ubyte type = 100; 985 } 986 987 static class SimpleC : SimpleB 988 { 989 @nonPacked string str; 990 uint num = uint.max; 991 } 992 993 SimpleC sc = new SimpleC; 994 value = sc.pack!true().unpack().value; 995 996 assert(value.toJSONValue().type == JSONType.object); 997 assert(value.toJSONValue().object.length == 3); 998 assert("flag" in value.toJSONValue().object); 999 assert(value.toJSONValue().object["flag"].type == (sc.flag ? JSONType.true_ : JSONType.false_)); 1000 assert("type" in value.toJSONValue().object); 1001 assert(value.toJSONValue().object["type"].type == JSONType.uinteger); 1002 assert(value.toJSONValue().object["type"].uinteger == sc.type); 1003 assert("num" in value.toJSONValue().object); 1004 assert(value.toJSONValue().object["num"].type == JSONType.uinteger); 1005 assert(value.toJSONValue().object["num"].uinteger == sc.num); 1006 1007 other = value.toJSONValue().fromJSONValue(); 1008 assert(value == other); 1009 } 1010 1011 1012 private: 1013 1014 1015 /** 1016 * A callback for type-mismatched error in cast conversion. 1017 */ 1018 @safe 1019 pure void onCastError() 1020 { 1021 throw new MessagePackException("Attempt to cast with another type"); 1022 }