1 /*
2  * Hunt - A refined core library for D programming language.
3  *
4  * Copyright (C) 2018-2019 HuntLabs
5  *
6  * Website: https://www.huntlabs.net/
7  *
8  * Licensed under the Apache-2.0 License.
9  *
10  */
11 
12 module hunt.serialization.JsonSerializer;
13 
14 import hunt.serialization.Common;
15 import hunt.logging.ConsoleLogger;
16 
17 
18 import std.algorithm : map;
19 import std.array;
20 import std.conv;
21 import std.datetime;
22 import std.json;
23 import std.stdio;
24 import std.traits;
25 
26 
27 enum MetaTypeName = "__metatype__";
28 
29 /* -------------------------------------------------------------------------- */
30 /*                                 Annotations                                */
31 /* -------------------------------------------------------------------------- */
32 
33 // https://github.com/FasterXML/jackson-annotations
34 // http://tutorials.jenkov.com/java-json/jackson-annotations.html
35 
36 enum JsonIgnore;
37 
38 struct JsonProperty {
39     string name;
40 }
41 
42 /**
43  * 
44  */
45 interface JsonSerializable {
46 
47     JSONValue jsonSerialize();
48 
49     void jsonDeserialize(const(JSONValue) value);
50 }
51 
52 
53 /**
54  * 
55  */
56 final class JsonSerializer {
57 
58     static T getItemAs(T, bool canThrow = false)(ref const(JSONValue) json, string name, 
59         T defaultValue = T.init) if (!is(T == void)) {
60         if (json.type() != JSONType.object) {            
61             return handleException!(T, canThrow)(json, "wrong member type", defaultValue);
62         }
63 
64         auto item = name in json;
65         if (item is null) {            
66             return handleException!(T, canThrow)(json, "wrong member type", defaultValue);
67         }
68         else {
69             return toObject!T(*item); // , defaultValue
70         }
71     }
72 
73     static T toObject(T, SerializationOptions options = SerializationOptions.Default)
74             (string json, T defaultValue = T.init) if (is(T == class)) {
75         return toObject!(T, options)(parseJSON(json));
76     }
77 
78     static T toObject(T, SerializationOptions options = SerializationOptions.Default)
79             (string json, T defaultValue = T.init) if (!is(T == class)) {
80         return toObject!(T, options.traverseBase(false))(parseJSON(json), defaultValue);
81     }
82 
83     /**
84      * Converts a `JSONValue` to an object of type `T` by filling its fields with the JSON's fields.
85      */
86     static T toObject(T, SerializationOptions options = SerializationOptions.Default)
87             (auto ref const(JSONValue) json, T defaultValue = T.init) 
88             if (is(T == class)) { // is(typeof(new T()))
89 
90         if(json.isNull())
91             return defaultValue;
92 
93         if (json.type() != JSONType.object) {
94             return handleException!(T, options.canThrow())(json, "wrong object type", defaultValue);
95         }
96 
97         static if(__traits(compiles, new T())) {
98             auto result = new T();
99 
100             static if(is(T : JsonSerializable)) {
101                 result.jsonDeserialize(json);
102             } else {
103                 try {
104                     deserializeObject!(T, options)(result, json);
105                 } catch (JSONException e) {
106                     return handleException!(T, options.canThrow())(json, e.msg, defaultValue);
107                 }
108             }
109             return result;
110         } else {
111             warningf("The %s does NOT define the default constructor. So a null will be returned.", typeid(T));
112             return defaultValue;          
113         }
114     }
115     
116 
117     /**
118      * struct
119      */
120     static T toObject(T, SerializationOptions options = SerializationOptions.Default)(
121             auto ref const(JSONValue) json, T defaultValue = T.init) 
122             if (is(T == struct) && !is(T == SysTime)) {
123 
124         if(json.isNull)
125             return defaultValue;
126 
127         JSONType jt = json.type();
128         if (jt != JSONType.object) {
129             return handleException!(T, options.canThrow())(json, "wrong object type", defaultValue);
130         }
131 
132         auto result = T();
133 
134         try {
135             static foreach (string member; FieldNameTuple!T) {
136                 deserializeMember!(member, options.traverseBase(false))(result, json);
137             }
138         } catch (JSONException e) {
139             return handleException!(T, options.canThrow())(json, e.msg, defaultValue);
140         }
141 
142         return result;
143     }
144 
145 
146     static void deserializeObject(T, SerializationOptions options = SerializationOptions.Default)
147             (ref T target, auto ref const(JSONValue) json)
148          if(is(T == struct)) {
149         enum SerializationOptions fixedOptions = options.traverseBase(false);
150         static foreach (string member; FieldNameTuple!T) {
151             // current fields
152             deserializeMember!(member, fixedOptions)(target, json);
153         }
154     }
155 
156     /**
157      * 
158      */
159     static void deserializeObject(T, SerializationOptions options = SerializationOptions.Default)
160             (T target, auto ref const(JSONValue) json) if(is(T == class)) {
161 
162         static foreach (string member; FieldNameTuple!T) {
163             // current fields
164             deserializeMember!(member, options)(target, json);
165         }
166 
167         // super fields
168         static if(options.traverseBase()) {
169             alias baseClasses = BaseClassesTuple!T;
170             alias BaseType = baseClasses[0];
171 
172             static if(baseClasses.length >= 1 && !is(BaseType == Object)) {
173                 debug(HUNT_DEBUG_MORE) {
174                     infof("TODO: deserializing fields in base %s for %s", BaseType.stringof, T.stringof);
175                 }
176                 auto jsonItemPtr = "super" in json;
177                 if(jsonItemPtr !is null) {
178                     deserializeObject!(BaseType, options)(target, *jsonItemPtr);
179                 }
180             }
181         }
182     }
183 
184     private static void deserializeMember(string member, SerializationOptions options, T)
185             (ref T target, auto ref const(JSONValue) json) {
186         
187         alias currentMember = __traits(getMember, T, member);
188         alias memberType = typeof(currentMember);
189         debug(HUNT_DEBUG_MORE) {
190             infof("deserializing member: %s %s", memberType.stringof, member);
191         }
192 
193         static if(hasUDA!(currentMember, Ignore) || hasUDA!(currentMember, JsonIgnore)) {
194             enum canDeserialize = false;
195             version(HUNT_DEBUG) {
196                 infof("Ignore a member: %s %s", memberType.stringof, member);
197             } 
198         } else static if(options.onlyPublic) {
199             static if (__traits(getProtection, currentMember) == "public") {
200                 enum canDeserialize = true;
201             } else {
202                 enum canDeserialize = false;
203             }
204         } else static if(is(memberType == interface) && !is(memberType : JsonSerializable)) {
205             enum canDeserialize = false;
206             version(HUNT_DEBUG) warning("skipped a interface member (not JsonSerializable): " ~ member);
207         } else {
208             enum canDeserialize = true;
209         }
210 
211         static if(canDeserialize) {
212             alias jsonPropertyUDAs = getUDAs!(currentMember, JsonProperty);
213             static if(jsonPropertyUDAs.length > 0) {
214                 enum PropertyName = jsonPropertyUDAs[0].name;
215                 enum jsonKeyName = (PropertyName.length == 0) ? member : PropertyName;
216             } else {
217                 enum jsonKeyName = member;
218             }
219 
220             auto jsonItemPtr = jsonKeyName in json;
221             if(jsonItemPtr is null) {
222                 version(HUNT_DEBUG) {
223                     if(jsonKeyName != member)
224                         warningf("No data available for member: %s as %s", member, jsonKeyName);
225                     else
226                         warningf("No data available for member: %s", member);
227                 }
228             } else {
229                 debug(HUNT_DEBUG_MORE) tracef("available data: %s = %s", member, jsonItemPtr.toString());
230                 static if(is(memberType == class)) {
231                     __traits(getMember, target, member) = toObject!(memberType, options)(*jsonItemPtr);
232                 } else {
233                     __traits(getMember, target, member) = toObject!(memberType, options)(*jsonItemPtr);
234                 }
235             }
236         }   
237     }
238 
239     /// JsonSerializable
240     static T toObject(T, SerializationOptions options = SerializationOptions.Default)(
241             auto ref const(JSONValue) json, 
242             T defaultValue = T.init) 
243             if(is(T == interface) && is(T : JsonSerializable)) {
244 
245         auto jsonItemPtr = MetaTypeName in json;
246         if(jsonItemPtr is null) {
247             warningf("Can't find 'type' item for interface %s", T.stringof);
248             return T.init;
249         }
250         string typeId = jsonItemPtr.str;
251         T t = cast(T) Object.factory(typeId);
252         if(t is null) {
253             warningf("Can't create instance for %s", T.stringof);
254         }
255         t.jsonDeserialize(json);
256         return t;
257     
258     }
259 
260     /// SysTime
261     static T toObject(T, SerializationOptions options = SerializationOptions.Default)(
262             auto ref const(JSONValue) json, 
263             T defaultValue = T.init) 
264             if(is(T == SysTime)) {
265   
266         JSONType jt = json.type();
267         if(jt == JSONType..string) {
268             return SysTime.fromSimpleString(json.str);
269         } else if(jt == JSONType.integer) {
270             return SysTime(json.integer);  // STD time
271         } else {
272             return handleException!(T, options.canThrow())(json, "wrong SysTime type", defaultValue);
273         }
274     }
275 
276     // static N toObject(N : Nullable!T, T, bool canThrow = false)(auto ref const(JSONValue) json) {
277 
278     //     return (json.type == JSONType.null_) ? N() : toObject!T(json).nullable;
279     // }
280 
281     /// JSONValue
282     static T toObject(T : JSONValue, SerializationOptions options = SerializationOptions.Default)(auto ref const(JSONValue) json) {
283         import std.typecons : nullable;
284         return json.nullable.get();
285     }
286 
287     /// ditto
288     static T toObject(T, SerializationOptions options = SerializationOptions.Default)
289             (auto ref const(JSONValue) json, T defaultValue = T.init) 
290             if (isNumeric!T || isSomeChar!T) {
291 
292         switch (json.type) {
293         case JSONType.null_, JSONType.false_:
294             return 0.to!T;
295 
296         case JSONType.true_:
297             return 1.to!T;
298 
299         case JSONType.float_:
300             return json.floating.to!T;
301 
302         case JSONType.integer:
303             return json.integer.to!T;
304 
305         case JSONType.uinteger:
306             return json.uinteger.to!T;
307 
308         case JSONType..string:
309             try {
310                 return json.str.to!T;
311             } catch(Exception ex) {
312                 return handleException!(T, options.canThrow())(json, ex.msg, defaultValue);
313             }
314 
315         default:
316             return handleException!(T, options.canThrow())(json, "", defaultValue);
317         }
318     }
319 
320     static T handleException(T, bool canThrow = false) (auto ref const(JSONValue) json, 
321         string message, T defaultValue = T.init) {
322         static if (canThrow) {
323             throw new JSONException(json.toString() ~ " is not a " ~ T.stringof ~ 
324                 " type. The inner exception: " ~ message);
325         } else {
326         version (HUNT_DEBUG)
327             warningf(" %s is not a %s type. Using the defaults instead! \n Exception: %s",
328                 json.toString(), T.stringof, message);
329             return defaultValue;
330         }
331     }
332 
333     /// bool
334     static T toObject(T, SerializationOptions options = SerializationOptions.Default)
335             (auto ref const(JSONValue) json) if (isBoolean!T) {
336 
337         switch (json.type) {
338         case JSONType.null_, JSONType.false_:
339             return false;
340 
341         case JSONType.float_:
342             return json.floating != 0;
343 
344         case JSONType.integer:
345             return json.integer != 0;
346 
347         case JSONType.uinteger:
348             return json.uinteger != 0;
349 
350         case JSONType..string:
351             return json.str.length > 0;
352 
353         default:
354             return true;
355         }
356     }
357 
358     /// string
359     static T toObject(T, SerializationOptions options = SerializationOptions.Default)
360             (auto ref const(JSONValue) json, T defaultValue = T.init)
361                 if (isSomeString!T || is(T : string) || is(T : wstring) || is(T : dstring)) {
362 
363         static if (is(T == enum)) {
364             foreach (member; __traits(allMembers, T)) {
365                 auto m = __traits(getMember, T, member);
366 
367                 if (json.str == m) {
368                     return m;
369                 }
370             }
371             return handleException!(T, options.canThrow())(json, 
372                 " is not a member of " ~ typeid(T).toString(), defaultValue);
373         } else {
374             if(json.type == JSONType..string) {
375                 static if(is(T == string)) {
376                     return json.str;
377                 } else {
378                     return to!T(json.str);
379                 }
380             } else if(json.type == JSONType.null_) {
381                 return T.init;
382             } else {
383                 return json.toString().to!T;
384             }
385             // return (json.type == JSONType.string ? json.str : json.toString()).to!T;
386         }
387     }
388 
389     /// Object array
390     static T toObject(T : U[], SerializationOptions options = SerializationOptions.Default, U)
391             (auto ref const(JSONValue) json, 
392             T defaultValue = T.init)
393             if (isArray!T && !isSomeString!T && !is(T : string) && !is(T
394                 : wstring) && !is(T : dstring)) {
395 
396         switch (json.type) {
397             case JSONType.null_:
398                 return [];
399 
400             case JSONType.false_:
401                 return [toObject!(U, options)(JSONValue(false))];
402 
403             case JSONType.true_:
404                 return [toObject!(U, options)(JSONValue(true))];
405 
406             case JSONType.array:
407                 return json.array
408                     .map!(value => toObject!(U, options)(value))
409                     .array
410                     .to!T;
411 
412             case JSONType.object:
413                 return handleException!(T, options.canThrow())(json, "", defaultValue);
414 
415             default:
416                 try {
417                     U obj = toObject!(U, options.canThrow(true))(json);
418                     return [obj];
419                 } catch(Exception ex) {
420                     warning(ex.msg);
421                     version(HUNT_DEBUG) warning(ex);
422                     if(options.canThrow)
423                         throw ex;
424                     else {
425                         return [];
426                     }
427                 }
428         }
429     }
430 
431     /// AssociativeArray
432     static T toObject(T : U[K], SerializationOptions options = SerializationOptions.Default, U, K)(
433             auto ref const(JSONValue) json, T defaultValue = T.init) 
434             if (isAssociativeArray!T) {
435         
436         U[K] result;
437 
438         switch (json.type) {
439         case JSONType.null_:
440             return result;
441 
442         case JSONType.object:
443             foreach (key, value; json.object) {
444                 result[key.to!K] = toObject!(U, options)(value);
445             }
446 
447             break;
448 
449         case JSONType.array:
450             foreach (key, value; json.array) {
451                 result[key.to!K] = toObject!(U, options)(value);
452             }
453 
454             break;
455 
456         default:
457             return handleException!(T, options.canThrow())(json, "", defaultValue);
458         }
459 
460         return result;
461     }
462 
463 
464     /* -------------------------------------------------------------------------- */
465     /*                                   toJson                                   */
466     /* -------------------------------------------------------------------------- */
467 
468     /**
469      * class
470      */
471     static JSONValue toJson(int depth=-1, T)(T value) if (is(T == class)) {
472         enum options = SerializationOptions().depth(depth);
473         return toJson!(options)(value);
474     }
475 
476 
477     /// ditto
478     static JSONValue toJson(SerializationOptions options, T)
479             (T value) if (is(T == class)) {
480                 
481         bool[size_t] serializationStates;
482         return toJsonImpl!(options)(value, serializationStates);
483     }
484 
485     /**
486      * Implements for class to json
487      */
488     private static JSONValue toJsonImpl(SerializationOptions options, T)
489             (T value, ref bool[size_t] serializationStates) if (is(T == class)) {
490         
491         debug(HUNT_DEBUG_MORE) {
492             info("======== current type: class " ~ T.stringof);
493             tracef("%s, T: %s",
494                 options, T.stringof);
495         }
496         static if(is(T : JsonSerializable)) {
497             // JsonSerializable first
498             return toJson!(JsonSerializable, IncludeMeta.no)(value);
499         } else {
500             JSONValue v = serializeObject!(options, T)(value, serializationStates);
501 
502             version(HUNT_DEBUG_MORE) {
503                 error(serializationStates);
504             }
505 
506             return v;
507         }
508     }
509 
510     deprecated("Using the other form of toJson(options) instead.")
511     static JSONValue toJson(T, TraverseBase traverseBase,
512             OnlyPublic onlyPublic = OnlyPublic.no, 
513             IncludeMeta includeMeta = IncludeMeta.no)
514             (T value) if (is(T == class)) {
515         enum options = SerializationOptions(onlyPublic, traverseBase, includeMeta);
516         bool[size_t] serializationStates;
517         return serializeObject!(options, T)(value, serializationStates);
518     }
519 
520 
521     deprecated("Using serializeObject(SerializationOptions) instead.")
522     static JSONValue serializeObject(OnlyPublic onlyPublic, TraverseBase traverseBase,
523         IncludeMeta includeMeta, T) (T value) if (is(T == class)) {
524 
525         enum options = SerializationOptions(onlyPublic, traverseBase, includeMeta);
526         bool[size_t] serializationStates;
527         return serializeObject!(options, T)(value, serializationStates);
528     }
529 
530     /**
531      * class object
532      */
533     static JSONValue serializeObject(SerializationOptions options = SerializationOptions.Full, T)
534             (T value, ref bool[size_t] serializationStates) if (is(T == class)) {
535         import std.traits : isSomeFunction, isType;
536 
537         debug(HUNT_DEBUG_MORE) {
538             info("======== current type: class " ~ T.stringof);
539             tracef("%s, T: %s", options, T.stringof);
540             // tracef("traverseBase = %s, onlyPublic = %s, includeMeta = %s, T: %s",
541             //     traverseBase, onlyPublic, includeMeta, T.stringof);
542         }
543 
544         if (value is null) {
545             version(HUNT_DEBUG) warning("value is null");
546             return JSONValue(null);
547         }
548 
549         static if(options.canCircularDetect) {
550 
551             size_t objHash = value.toHash() + hashOf(T.stringof);
552             auto itemPtr = objHash in serializationStates;
553             if(itemPtr !is null && *itemPtr) {
554                 debug(HUNT_DEBUG_MORE) warningf("%s serialized.", T.stringof);
555                 return JSONValue(null);
556             }
557             
558             serializationStates[objHash] = true;
559         }
560 
561         auto result = JSONValue();
562         static if(options.includeMeta) {
563             result[MetaTypeName] = typeid(T).name;
564         }
565         // debug(HUNT_DEBUG_MORE) pragma(msg, "======== current type: class " ~ T.stringof);
566         
567         // super fields
568         static if(options.traverseBase) {
569             alias baseClasses = BaseClassesTuple!T;
570             static if(baseClasses.length >= 1) {
571 
572                 alias BaseType = baseClasses[0];
573                 debug(HUNT_DEBUG_MORE) {
574                     tracef("BaseType: %s", BaseType.stringof);
575                 }
576                 static if(!is(BaseType == Object)) {
577                     JSONValue superResult = serializeObject!(options, BaseType)(value, serializationStates);
578                     if(!superResult.isNull)
579                         result["super"] = superResult;
580                 }
581             }
582         }
583         
584         // current fields
585 		static foreach (string member; FieldNameTuple!T) {
586             serializeMember!(member, options)(value, result, serializationStates);
587         }
588 
589         return result;
590     }
591 
592     /**
593      * struct
594      */
595     
596     static JSONValue toJson(SerializationOptions options = SerializationOptions(), T)(T value)
597             if (is(T == struct) && !is(T == SysTime)) {
598         bool[size_t] serializationStates;
599         return toJsonImpl!(options)(value, serializationStates);
600     }
601 
602     /**
603      * Implements for struct to json
604      */
605     static JSONValue toJsonImpl(SerializationOptions options = SerializationOptions(), T)(T value, 
606             ref bool[size_t] serializationStates) if (is(T == struct) && !is(T == SysTime)) {
607 
608         static if(is(T == JSONValue)) {
609             return value;
610         } else {
611             auto result = JSONValue();
612             // debug(HUNT_DEBUG_MORE) pragma(msg, "======== current type: struct " ~ T.stringof);
613             debug(HUNT_DEBUG_MORE) info("======== current type: struct " ~ T.stringof);
614                 
615             static foreach (string member; FieldNameTuple!T) {
616                 serializeMember!(member, options)(value, result, serializationStates);
617             }
618 
619             return result;
620         }
621     }
622 
623     /**
624      * Object's memeber
625      */
626     private static void serializeMember(string member, 
627             SerializationOptions options = SerializationOptions.Default, T)
628             (T obj, ref JSONValue result, ref bool[size_t] serializationStates) {
629 
630         // debug(HUNT_DEBUG_MORE) pragma(msg, "\tfield=" ~ member);
631 
632         alias currentMember = __traits(getMember, T, member);
633 
634         static if(options.onlyPublic) {
635             static if (__traits(getProtection, currentMember) == "public") {
636                 enum canSerialize = true;
637             } else {
638                 enum canSerialize = false;
639             }
640         } else static if(hasUDA!(currentMember, Ignore) || hasUDA!(currentMember, JsonIgnore)) {
641             enum canSerialize = false;
642         } else {
643             enum canSerialize = true;
644         }
645         
646         debug(HUNT_DEBUG_MORE) {
647             tracef("name: %s, %s", member, options);
648         }
649 
650         static if(canSerialize) {
651             alias memberType = typeof(currentMember);
652             debug(HUNT_DEBUG_MORE) infof("memberType: %s in %s", memberType.stringof, T.stringof);
653 
654             static if(is(memberType == interface) && !is(memberType : JsonSerializable)) {
655                 version(HUNT_DEBUG) warning("skipped a interface member(not JsonSerializable): " ~ member);
656             } else {
657                 auto m = __traits(getMember, obj, member);
658 
659                 alias jsonPropertyUDAs = getUDAs!(currentMember, JsonProperty);
660                 static if(jsonPropertyUDAs.length > 0) {
661                     enum PropertyName = jsonPropertyUDAs[0].name;
662                     enum jsonKeyName = (PropertyName.length == 0) ? member : PropertyName;
663                 } else {
664                     enum jsonKeyName = member;
665                 }
666 
667                 auto json = serializeMember!(options)(m, serializationStates);
668 
669                 debug(HUNT_DEBUG_MORE) {
670                     tracef("name: %s, value: %s", member, json.toString());
671                 }
672 
673                 bool canSetValue = true;
674                 if(json.isNull) {
675                     static if(options.ignoreNull) {
676                         canSetValue = false;
677                     }
678                 }
679 
680                 if (canSetValue) {
681                         // trace(result);
682                     if(!result.isNull) {
683                         auto jsonItemPtr = jsonKeyName in result;
684                         if(jsonItemPtr !is null) {
685                             version(HUNT_DEBUG) warning("overrided field: " ~ member);
686                         }
687                     }
688                     result[jsonKeyName] = json;
689                 }
690             }
691         } else {
692             debug(HUNT_DEBUG_MORE) tracef("skipped member, name: %s", member);
693         }
694     }
695 
696     private static JSONValue serializeMember(SerializationOptions options, T)(T m, 
697             ref bool[size_t] serializationStates) {
698         JSONValue json;
699         enum depth = options.depth;
700         static if(is(T == interface) && is(T : JsonSerializable)) {
701             static if(depth == -1 || depth > 0) { json = toJson!(JsonSerializable)(m);}
702         } else static if(is(T == SysTime)) {
703             json = toJson!SysTime(m);
704         // } else static if(isSomeString!T) {
705         //     json = toJson(m);
706         } else static if(is(T == class)) {
707             if(m !is null) {
708                 json = serializeObjectMember!(options)(m, serializationStates);
709             }
710         } else static if(is(T == struct)) {
711             json = serializeObjectMember!(options)(m, serializationStates);
712         } else static if(is(T : U[], U)) { 
713             if(m is null) {
714                 static if(!options.ignoreNull) {
715                     static if(isSomeString!T) {
716                         json = toJson(m);
717                     } else {
718                         json = JSONValue[].init;
719                     }
720                 }
721             } else {
722                 static if (is(U == class) || is(U == struct) || is(U == interface)) {
723                     // class[] obj; struct[] obj;
724                     json = serializeObjectMember!(options)(m, serializationStates);
725                 } else {
726                     json = toJson(m);
727                 }
728             }
729         } else {
730             json = toJson(m);
731         }
732 
733         return json;
734         
735     }
736 
737     private static JSONValue serializeObjectMember(SerializationOptions options = 
738             SerializationOptions.Default, T)(ref T m, ref bool[size_t] serializationStates) {
739         enum depth = options.depth;
740         static if(depth > 0) {
741             enum SerializationOptions memeberOptions = options.depth(options.depth-1);
742             return toJsonImpl!(memeberOptions)(m, serializationStates);
743         } else static if(depth == -1) {
744             return toJsonImpl!(options)(m, serializationStates);
745         } else {
746             return JSONValue.init;
747         }
748     }
749 
750     /**
751      * SysTime
752      */
753     static JSONValue toJson(T)(T value, bool asInteger=true) if(is(T == SysTime)) {
754         if(asInteger)
755             return JSONValue(value.stdTime()); // STD time
756         else 
757             return JSONValue(value.toString());
758     }
759 
760     /**
761      * JsonSerializable
762      */
763     static JSONValue toJson(T, IncludeMeta includeMeta = IncludeMeta.yes)
764                     (T value) if (is(T == interface) && is(T : JsonSerializable)) {
765 
766         debug(HUNT_DEBUG_MORE) {
767             if(value is null) {
768                 infof("======== current type: interface = %s, Object = null", 
769                     T.stringof);
770             } else {
771                 infof("======== current type: interface = %s, Object = %s", 
772                     T.stringof, typeid(cast(Object)value).name);
773             }
774         }
775         
776         if(value is null) {
777             return JSONValue(null);
778         }
779 
780         JSONValue v = value.jsonSerialize();
781         static if(includeMeta) {
782             auto itemPtr = MetaTypeName in v;
783             if(itemPtr is null)
784                 v[MetaTypeName] = typeid(cast(Object)value).name;
785         }
786         // TODO: Tasks pending completion -@zhangxueping at 2019-09-28T07:45:09+08:00
787         // remove the MetaTypeName memeber
788         debug(HUNT_DEBUG_MORE) trace(v.toString());
789         return v;
790     }
791 
792     /**
793      * Basic type
794      */
795     static JSONValue toJson(T)(T value) if (isBasicType!T) {
796         static if(is(T == double) || is(T == float)) {
797             import std.math : isNaN;
798             if(isNaN(value)) {
799                 warning("Uninitialized float/double value. It will be set to zero.");
800                 value = 0;
801             }
802         }
803         return JSONValue(value);
804     }
805 
806     /**
807      * T[]
808      */
809     static JSONValue toJson(SerializationOptions options = SerializationOptions.Default, T: U[], U)(T value) {
810         bool[size_t] serializationStates;
811         return toJsonImpl!(options)(value, serializationStates);
812     }
813 
814     private static JSONValue toJsonImpl(SerializationOptions options = SerializationOptions.Default, T: U[], U)(T value, 
815             ref bool[size_t] serializationStates) {
816 
817         static if(is(U == class)) { // class[]
818             if(value is null) {
819                 return JSONValue(JSONValue[].init);
820             } else {
821                 return JSONValue(value.map!(item => toJsonImpl!(options)(item, serializationStates))()
822                         .map!(json => json.isNull ? JSONValue(null) : json).array);
823             }
824         } else static if(is(U == struct)) { // struct[]
825             if(value is null) {
826                 return JSONValue(JSONValue[].init);
827             } else {
828                 static if(is(U == SysTime)) {
829                     return JSONValue(value.map!(item => toJson(item))()
830                             .map!(json => json.isNull ? JSONValue(null) : json).array);
831                 } else {
832                     return JSONValue(value.map!(item => toJsonImpl!(options)(item, serializationStates))()
833                             .map!(json => json.isNull ? JSONValue(null) : json).array);
834                 }
835             }
836         } else static if(is(U : S[], S)) { // S[][]
837             if(value is null) 
838                 return JSONValue(JSONValue[].init);
839 
840             JSONValue[] items;
841             foreach(S[] element; value) {
842                 static if(is(S == struct) || is(S == class)) {
843                     items ~= toJsonImpl(element, serializationStates);
844                 } else {
845                     items ~= toJson(element);
846                 }
847             }
848 
849             return JSONValue(items);
850         } else {
851             return JSONValue(value);
852         }
853     }
854 
855 
856     /**
857      * V[K]
858      */
859     static JSONValue toJson(SerializationOptions options = SerializationOptions.Default,
860             T : V[K], V, K)(T value) {
861         bool[size_t] serializationStates;
862         return toJsonImpl!(options)(value, serializationStates);
863     }
864 
865     private static JSONValue toJsonImpl(SerializationOptions options = SerializationOptions.Default,
866             T : V[K], V, K)(T value, ref bool[size_t] serializationStates) {
867         auto result = JSONValue();
868 
869         foreach (key; value.keys) {
870             static if(is(V == SysTime)) {
871                 auto json = toJson(value[key]);
872             } else static if(is(V == class) || is(V == struct) || is(V == interface)) {
873                 auto json = toJsonImpl!(options)(value[key], serializationStates);
874             } else {
875                 auto json = toJson(value[key]);
876             }
877             result[key.to!string] = json.isNull ? JSONValue(null) : json;
878         }
879 
880         return result;
881     }
882 
883     deprecated("Using toObject instead.")
884     alias fromJson = toObject;
885 }
886 
887 
888 alias toJson = JsonSerializer.toJson;
889 alias toObject = JsonSerializer.toObject;
890 
891 
892 deprecated("Using toObject instead.")
893 alias fromJson = JsonSerializer.toObject;