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