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 }