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;
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_MORE) 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_MORE) {
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     /// Dynamic 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 (isDynamicArray!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     /// Static array
432     static T toObject(T : U[], SerializationOptions options = SerializationOptions.Default, U)
433             (auto ref const(JSONValue) json, 
434             T defaultValue = T.init)
435             if (isStaticArray !T && !isSomeString!T && !is(T : string) && !is(T
436                 : wstring) && !is(T : dstring)) {
437 
438         enum ArrayLength = T.length;
439 
440         switch (json.type) {
441             case JSONType.null_:
442                 return T.init;
443 
444             case JSONType.false_:
445                 U[] r = [toObject!(U, options)(JSONValue(false))];
446                 assert(r.length >= ArrayLength);
447                 return r[0..ArrayLength];
448 
449             case JSONType.true_:
450                 U[] r = [toObject!(U, options)(JSONValue(true))];
451                 assert(r.length >= ArrayLength);
452                 return r[0..ArrayLength];
453 
454             case JSONType.array:
455                 U[] r = json.array
456                     .map!(value => toObject!(U, options)(value))
457                     .array
458                     .to!(U[]);
459                 assert(r.length >= ArrayLength);
460                 return r[0..ArrayLength];
461 
462             case JSONType.object:
463                 return handleException!(T, options.canThrow())(json, "", defaultValue);
464 
465             default:
466 
467                 return handleException!(T, options.canThrow())(json, "", defaultValue);            
468                 // try {
469                 //     U obj = toObject!(U, options.canThrow(true))(json);
470                 //     return [obj];
471                 // } catch(Exception ex) {
472                 //     warning(ex.msg);
473                 //     version(HUNT_DEBUG) warning(ex);
474                 //     if(options.canThrow)
475                 //         throw ex;
476                 //     else {
477                 //         return [];
478                 //     }
479                 // }
480         }
481     }
482 
483     /// AssociativeArray
484     static T toObject(T : U[K], SerializationOptions options = SerializationOptions.Default, U, K)(
485             auto ref const(JSONValue) json, T defaultValue = T.init) 
486             if (isAssociativeArray!T) {
487         
488         U[K] result;
489 
490         switch (json.type) {
491         case JSONType.null_:
492             return result;
493 
494         case JSONType.object:
495             foreach (key, value; json.object) {
496                 result[key.to!K] = toObject!(U, options)(value);
497             }
498 
499             break;
500 
501         case JSONType.array:
502             foreach (key, value; json.array) {
503                 result[key.to!K] = toObject!(U, options)(value);
504             }
505 
506             break;
507 
508         default:
509             return handleException!(T, options.canThrow())(json, "", defaultValue);
510         }
511 
512         return result;
513     }
514 
515 
516     /* -------------------------------------------------------------------------- */
517     /*                                   toJson                                   */
518     /* -------------------------------------------------------------------------- */
519 
520     /**
521      * class
522      */
523     static JSONValue toJson(int depth=-1, T)(T value) if (is(T == class)) {
524         enum options = SerializationOptions().depth(depth);
525         return toJson!(options)(value);
526     }
527 
528 
529     /// ditto
530     static JSONValue toJson(SerializationOptions options, T)
531             (T value) if (is(T == class)) {
532                 
533         bool[size_t] serializationStates;
534         return toJsonImpl!(options)(value, serializationStates);
535     }
536 
537     /**
538      * Implements for class to json
539      */
540     private static JSONValue toJsonImpl(SerializationOptions options, T)
541             (T value, ref bool[size_t] serializationStates) if (is(T == class)) {
542         
543         debug(HUNT_DEBUG_MORE) {
544             info("======== current type: class " ~ T.stringof);
545             tracef("%s, T: %s",
546                 options, T.stringof);
547         }
548         static if(is(T : JsonSerializable)) {
549             // JsonSerializable first
550             return toJson!(JsonSerializable, IncludeMeta.no)(value);
551         } else {
552             JSONValue v = serializeObject!(options, T)(value, serializationStates);
553 
554             version(HUNT_DEBUG_MORE) {
555                 error(serializationStates);
556             }
557 
558             return v;
559         }
560     }
561 
562     deprecated("Using the other form of toJson(options) instead.")
563     static JSONValue toJson(T, TraverseBase traverseBase,
564             OnlyPublic onlyPublic = OnlyPublic.no, 
565             IncludeMeta includeMeta = IncludeMeta.no)
566             (T value) if (is(T == class)) {
567         enum options = SerializationOptions(onlyPublic, traverseBase, includeMeta);
568         bool[size_t] serializationStates;
569         return serializeObject!(options, T)(value, serializationStates);
570     }
571 
572 
573     deprecated("Using serializeObject(SerializationOptions) instead.")
574     static JSONValue serializeObject(OnlyPublic onlyPublic, TraverseBase traverseBase,
575         IncludeMeta includeMeta, T) (T value) if (is(T == class)) {
576 
577         enum options = SerializationOptions(onlyPublic, traverseBase, includeMeta);
578         bool[size_t] serializationStates;
579         return serializeObject!(options, T)(value, serializationStates);
580     }
581 
582     /**
583      * class object
584      */
585     static JSONValue serializeObject(SerializationOptions options = SerializationOptions.Full, T)
586             (T value, ref bool[size_t] serializationStates) if (is(T == class)) {
587         import std.traits : isSomeFunction, isType;
588 
589         debug(HUNT_DEBUG_MORE) {
590             info("======== current type: class " ~ T.stringof);
591             tracef("%s, T: %s", options, T.stringof);
592             // tracef("traverseBase = %s, onlyPublic = %s, includeMeta = %s, T: %s",
593             //     traverseBase, onlyPublic, includeMeta, T.stringof);
594         }
595 
596         if (value is null) {
597             version(HUNT_DEBUG) warning("value is null");
598             return JSONValue(null);
599         }
600 
601         static if(options.canCircularDetect) {
602 
603             size_t objHash = value.toHash() + hashOf(T.stringof);
604             auto itemPtr = objHash in serializationStates;
605             if(itemPtr !is null && *itemPtr) {
606                 debug(HUNT_DEBUG_MORE) warningf("%s serialized.", T.stringof);
607                 return JSONValue(null);
608             }
609             
610             serializationStates[objHash] = true;
611         }
612 
613         auto result = JSONValue();
614         static if(options.includeMeta) {
615             result[MetaTypeName] = typeid(T).name;
616         }
617         // debug(HUNT_DEBUG_MORE) pragma(msg, "======== current type: class " ~ T.stringof);
618         
619         // super fields
620         static if(options.traverseBase) {
621             alias baseClasses = BaseClassesTuple!T;
622             static if(baseClasses.length >= 1) {
623 
624                 alias BaseType = baseClasses[0];
625                 debug(HUNT_DEBUG_MORE) {
626                     tracef("BaseType: %s", BaseType.stringof);
627                 }
628                 static if(!is(BaseType == Object)) {
629                     JSONValue superResult = serializeObject!(options, BaseType)(value, serializationStates);
630                     if(!superResult.isNull)
631                         result["super"] = superResult;
632                 }
633             }
634         }
635         
636         // current fields
637 		static foreach (string member; FieldNameTuple!T) {
638             serializeMember!(member, options)(value, result, serializationStates);
639         }
640 
641         return result;
642     }
643 
644     /**
645      * struct
646      */
647     
648     static JSONValue toJson(SerializationOptions options = SerializationOptions(), T)(T value)
649             if (is(T == struct) && !is(T == SysTime)) {
650         bool[size_t] serializationStates;
651         return toJsonImpl!(options)(value, serializationStates);
652     }
653 
654     /**
655      * Implements for struct to json
656      */
657     static JSONValue toJsonImpl(SerializationOptions options = SerializationOptions(), T)(T value, 
658             ref bool[size_t] serializationStates) if (is(T == struct) && !is(T == SysTime)) {
659 
660         static if(is(T == JSONValue)) {
661             return value;
662         } else {
663             auto result = JSONValue();
664             // debug(HUNT_DEBUG_MORE) pragma(msg, "======== current type: struct " ~ T.stringof);
665             debug(HUNT_DEBUG_MORE) info("======== current type: struct " ~ T.stringof);
666                 
667             static foreach (string member; FieldNameTuple!T) {
668                 serializeMember!(member, options)(value, result, serializationStates);
669             }
670 
671             return result;
672         }
673     }
674 
675     /**
676      * Object's memeber
677      */
678     private static void serializeMember(string member, 
679             SerializationOptions options = SerializationOptions.Default, T)
680             (T obj, ref JSONValue result, ref bool[size_t] serializationStates) {
681 
682         // debug(HUNT_DEBUG_MORE) pragma(msg, "\tfield=" ~ member);
683 
684         alias currentMember = __traits(getMember, T, member);
685 
686         static if(options.onlyPublic) {
687             static if (__traits(getProtection, currentMember) == "public") {
688                 enum canSerialize = true;
689             } else {
690                 enum canSerialize = false;
691             }
692         } else static if(hasUDA!(currentMember, Ignore) || hasUDA!(currentMember, JsonIgnore)) {
693             enum canSerialize = false;
694         } else {
695             enum canSerialize = true;
696         }
697         
698         debug(HUNT_DEBUG_MORE) {
699             tracef("name: %s, %s", member, options);
700         }
701 
702         static if(canSerialize) {
703             alias memberType = typeof(currentMember);
704             debug(HUNT_DEBUG_MORE) infof("memberType: %s in %s", memberType.stringof, T.stringof);
705 
706             static if(is(memberType == interface) && !is(memberType : JsonSerializable)) {
707                 version(HUNT_DEBUG_MORE) warning("skipped a interface member(not JsonSerializable): " ~ member);
708             } else {
709                 auto m = __traits(getMember, obj, member);
710 
711                 alias jsonPropertyUDAs = getUDAs!(currentMember, JsonProperty);
712                 static if(jsonPropertyUDAs.length > 0) {
713                     enum PropertyName = jsonPropertyUDAs[0].name;
714                     enum jsonKeyName = (PropertyName.length == 0) ? member : PropertyName;
715                 } else {
716                     enum jsonKeyName = member;
717                 }
718 
719                 auto json = serializeMember!(options)(m, serializationStates);
720 
721                 debug(HUNT_DEBUG_MORE) {
722                     tracef("name: %s, value: %s", member, json.toString());
723                 }
724 
725                 bool canSetValue = true;
726                 if(json.isNull) {
727                     static if(options.ignoreNull) {
728                         canSetValue = false;
729                     }
730                 }
731 
732                 if (canSetValue) {
733                         // trace(result);
734                     if(!result.isNull) {
735                         auto jsonItemPtr = jsonKeyName in result;
736                         if(jsonItemPtr !is null) {
737                             version(HUNT_DEBUG) warning("overrided field: " ~ member);
738                         }
739                     }
740                     result[jsonKeyName] = json;
741                 }
742             }
743         } else {
744             debug(HUNT_DEBUG_MORE) tracef("skipped member, name: %s", member);
745         }
746     }
747 
748     private static JSONValue serializeMember(SerializationOptions options, T)(T m, 
749             ref bool[size_t] serializationStates) {
750         JSONValue json;
751         enum depth = options.depth;
752         static if(is(T == interface) && is(T : JsonSerializable)) {
753             static if(depth == -1 || depth > 0) { json = toJson!(JsonSerializable)(m);}
754         } else static if(is(T == SysTime)) {
755             json = toJson!SysTime(m);
756         // } else static if(isSomeString!T) {
757         //     json = toJson(m);
758         } else static if(is(T == class)) {
759             if(m !is null) {
760                 json = serializeObjectMember!(options)(m, serializationStates);
761             }
762         } else static if(is(T == struct)) {
763             json = serializeObjectMember!(options)(m, serializationStates);
764         } else static if(is(T : U[], U)) { 
765             static if(isStaticArray!T) {
766                 static if (is(U == class) || is(U == struct) || is(U == interface)) {
767                     // class[] obj; struct[] obj;
768                     json = serializeObjectMember!(options)(m, serializationStates);
769                 } else {
770                     json = toJson(m);
771                 }
772             } else {
773                 if(m is null) {
774                     static if(!options.ignoreNull) {
775                         static if(isSomeString!T) {
776                             json = toJson(m);
777                         } else {
778                             json = JSONValue[].init;
779                         }
780                     }
781                 } else {
782                     static if (is(U == class) || is(U == struct) || is(U == interface)) {
783                         // class[] obj; struct[] obj;
784                         json = serializeObjectMember!(options)(m, serializationStates);
785                     } else {
786                         json = toJson(m);
787                     }
788                 }
789             }
790         } else {
791             json = toJson(m);
792         }
793 
794         return json;
795         
796     }
797 
798     private static JSONValue serializeObjectMember(SerializationOptions options = 
799             SerializationOptions.Default, T)(ref T m, ref bool[size_t] serializationStates) {
800         enum depth = options.depth;
801         static if(depth > 0) {
802             enum SerializationOptions memeberOptions = options.depth(options.depth-1);
803             return toJsonImpl!(memeberOptions)(m, serializationStates);
804         } else static if(depth == -1) {
805             return toJsonImpl!(options)(m, serializationStates);
806         } else {
807             return JSONValue.init;
808         }
809     }
810 
811     /**
812      * SysTime
813      */
814     static JSONValue toJson(T)(T value, bool asInteger=true) if(is(T == SysTime)) {
815         if(asInteger)
816             return JSONValue(value.stdTime()); // STD time
817         else 
818             return JSONValue(value.toString());
819     }
820 
821     /**
822      * JsonSerializable
823      */
824     static JSONValue toJson(T, IncludeMeta includeMeta = IncludeMeta.yes)
825                     (T value) if (is(T == interface) && is(T : JsonSerializable)) {
826 
827         debug(HUNT_DEBUG_MORE) {
828             if(value is null) {
829                 infof("======== current type: interface = %s, Object = null", 
830                     T.stringof);
831             } else {
832                 infof("======== current type: interface = %s, Object = %s", 
833                     T.stringof, typeid(cast(Object)value).name);
834             }
835         }
836         
837         if(value is null) {
838             return JSONValue(null);
839         }
840 
841         JSONValue v = value.jsonSerialize();
842         static if(includeMeta) {
843             auto itemPtr = MetaTypeName in v;
844             if(itemPtr is null)
845                 v[MetaTypeName] = typeid(cast(Object)value).name;
846         }
847         // TODO: Tasks pending completion -@zhangxueping at 2019-09-28T07:45:09+08:00
848         // remove the MetaTypeName memeber
849         debug(HUNT_DEBUG_MORE) trace(v.toString());
850         return v;
851     }
852 
853     /**
854      * Basic type
855      */
856     static JSONValue toJson(T)(T value) if (isBasicType!T) {
857         static if(is(T == double) || is(T == float)) {
858             import std.math : isNaN;
859             if(isNaN(value)) {
860                 warning("Uninitialized float/double value. It will be set to zero.");
861                 value = 0;
862             }
863         }
864         return JSONValue(value);
865     }
866 
867     /**
868      * T[]
869      */
870     static JSONValue toJson(SerializationOptions options = SerializationOptions.Default, T: U[], U)(T value) {
871         bool[size_t] serializationStates;
872         return toJsonImpl!(options)(value, serializationStates);
873     }
874 
875     private static JSONValue toJsonImpl(SerializationOptions options = SerializationOptions.Default, T: U[], U)(T data, 
876             ref bool[size_t] serializationStates) if(isStaticArray!T) {
877         
878         U[] value = data[0..$];
879         return toJsonImpl!(options)(value, serializationStates);
880     }
881 
882     private static JSONValue toJsonImpl(SerializationOptions options = SerializationOptions.Default, T: U[], U)(T value, 
883             ref bool[size_t] serializationStates) if(isDynamicArray!T) {
884 
885         static if(is(U == class)) { // class[]
886             if(value is null) {
887                 return JSONValue(JSONValue[].init);
888             } else {
889                 return JSONValue(value.map!(item => toJsonImpl!(options)(item, serializationStates))()
890                         .map!(json => json.isNull ? JSONValue(null) : json).array);
891             }
892         } else static if(is(U == struct)) { // struct[]
893             if(value is null) {
894                 return JSONValue(JSONValue[].init);
895             } else {
896                 static if(is(U == SysTime)) {
897                     return JSONValue(value.map!(item => toJson(item))()
898                             .map!(json => json.isNull ? JSONValue(null) : json).array);
899                 } else {
900                     return JSONValue(value.map!(item => toJsonImpl!(options)(item, serializationStates))()
901                             .map!(json => json.isNull ? JSONValue(null) : json).array);
902                 }
903             }
904         } else static if(is(U : S[], S)) { // S[][]
905             if(value is null) 
906                 return JSONValue(JSONValue[].init);
907 
908             JSONValue[] items;
909             foreach(S[] element; value) {
910                 static if(is(S == struct) || is(S == class)) {
911                     items ~= toJsonImpl(element, serializationStates);
912                 } else {
913                     items ~= toJson(element);
914                 }
915             }
916 
917             return JSONValue(items);
918         } else {
919             return JSONValue(value);
920         }
921     }
922 
923 
924     /**
925      * V[K]
926      */
927     static JSONValue toJson(SerializationOptions options = SerializationOptions.Default,
928             T : V[K], V, K)(T value) {
929         bool[size_t] serializationStates;
930         return toJsonImpl!(options)(value, serializationStates);
931     }
932 
933     private static JSONValue toJsonImpl(SerializationOptions options = SerializationOptions.Default,
934             T : V[K], V, K)(T value, ref bool[size_t] serializationStates) {
935         auto result = JSONValue();
936 
937         foreach (key; value.keys) {
938             static if(is(V == SysTime)) {
939                 auto json = toJson(value[key]);
940             } else static if(is(V == class) || is(V == struct) || is(V == interface)) {
941                 auto json = toJsonImpl!(options)(value[key], serializationStates);
942             } else {
943                 auto json = toJson(value[key]);
944             }
945             result[key.to!string] = json.isNull ? JSONValue(null) : json;
946         }
947 
948         return result;
949     }
950 
951     deprecated("Using toObject instead.")
952     alias fromJson = toObject;
953 }
954 
955 
956 alias toJson = JsonSerializer.toJson;
957 alias toObject = JsonSerializer.toObject;
958 
959 
960 deprecated("Using toObject instead.")
961 alias fromJson = JsonSerializer.toObject;