1 /*
2  * Geario - A cross-platform abstraction library with asynchronous I/O.
3  *
4  * Copyright (C) 2021-2022 Kerisy.com
5  *
6  * Website: https://www.kerisy.com
7  *
8  * Licensed under the Apache-2.0 License.
9  *
10  */
11 
12 module geario.serialization.JsonSerializer;
13 
14 import geario.serialization.Common;
15 import geario.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             log.warn("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(GEAR_DEBUG_MORE) {
174                     log.info("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(GEAR_DEBUG_MORE) {
190             log.info("deserializing member: %s %s", memberType.stringof, member);
191         }
192 
193         static if(hasUDA!(currentMember, Ignore) || hasUDA!(currentMember, JsonIgnore)) {
194             enum canDeserialize = false;
195             version(GEAR_DEBUG) {
196                 log.info("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(GEAR_DEBUG) log.warn("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(GEAR_DEBUG) {
223                     if(jsonKeyName != member)
224                         log.warn("No data available for member: %s as %s", member, jsonKeyName);
225                     else
226                         log.warn("No data available for member: %s", member);
227                 }
228             } else {
229                 debug(GEAR_DEBUG_MORE) log.trace("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             log.warn("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             log.warn("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 ~ " type");
324         } else {
325         version (GEAR_DEBUG)
326             log.warn(" %s is not a %s type. Using the defaults instead! \n Exception: %s",
327                 json.toString(), T.stringof, message);
328             return defaultValue;
329         }
330     }
331 
332     /// bool
333     static T toObject(T, SerializationOptions options = SerializationOptions.Default)
334             (auto ref const(JSONValue) json) if (isBoolean!T) {
335 
336         switch (json.type) {
337         case JSONType.null_, JSONType.false_:
338             return false;
339 
340         case JSONType.float_:
341             return json.floating != 0;
342 
343         case JSONType.integer:
344             return json.integer != 0;
345 
346         case JSONType.uinteger:
347             return json.uinteger != 0;
348 
349         case JSONType..string:
350             return json.str.length > 0;
351 
352         default:
353             return true;
354         }
355     }
356 
357     /// string
358     static T toObject(T, SerializationOptions options = SerializationOptions.Default)
359             (auto ref const(JSONValue) json, T defaultValue = T.init)
360                 if (isSomeString!T || is(T : string) || is(T : wstring) || is(T : dstring)) {
361 
362         static if (is(T == enum)) {
363             foreach (member; __traits(allMembers, T)) {
364                 auto m = __traits(getMember, T, member);
365 
366                 if (json.str == m) {
367                     return m;
368                 }
369             }
370             return handleException!(T, options.CanThrow())(json, 
371                 " is not a member of " ~ typeid(T).toString(), defaultValue);
372         } else {
373             return (json.type == JSONType..string ? json.str : json.toString()).to!T;
374         }
375     }
376 
377     /// Object array
378     static T toObject(T : U[], SerializationOptions options = SerializationOptions.Default, U)
379             (auto ref const(JSONValue) json, 
380             T defaultValue = T.init)
381             if (isArray!T && !isSomeString!T && !is(T : string) && !is(T
382                 : wstring) && !is(T : dstring)) {
383 
384         switch (json.type) {
385             case JSONType.null_:
386                 return [];
387 
388             case JSONType.false_:
389                 return [toObject!(U, options)(JSONValue(false))];
390 
391             case JSONType.true_:
392                 return [toObject!(U, options)(JSONValue(true))];
393 
394             case JSONType.array:
395                 return json.array
396                     .map!(value => toObject!(U, options)(value))
397                     .array
398                     .to!T;
399 
400             case JSONType.object:
401                 return handleException!(T, options.CanThrow())(json, "", defaultValue);
402 
403             default:
404                 try {
405                     U obj = toObject!(U, options.CanThrow(true))(json);
406                     return [obj];
407                 } catch(Exception ex) {
408                     log.warn(ex.msg);
409                     version(GEAR_DEBUG) log.warn(ex);
410                     if(options.CanThrow)
411                         throw ex;
412                     else {
413                         return [];
414                     }
415                 }
416         }
417     }
418 
419     /// AssociativeArray
420     static T toObject(T : U[K], SerializationOptions options = SerializationOptions.Default, U, K)(
421             auto ref const(JSONValue) json, T defaultValue = T.init) 
422             if (isAssociativeArray!T) {
423         
424         U[K] result;
425 
426         switch (json.type) {
427         case JSONType.null_:
428             return result;
429 
430         case JSONType.object:
431             foreach (key, value; json.object) {
432                 log.warn(typeid(value));
433                 result[key.to!K] = toObject!(U, options)(value);
434             }
435 
436             break;
437 
438         case JSONType.array:
439             foreach (key, value; json.array) {
440                 result[key.to!K] = toObject!(U, options)(value);
441             }
442 
443             break;
444 
445         default:
446             return handleException!(T, options.CanThrow())(json, "", defaultValue);
447         }
448 
449         return result;
450     }
451 
452 
453     /* -------------------------------------------------------------------------- */
454     /*                                   toJson                                   */
455     /* -------------------------------------------------------------------------- */
456 
457     /**
458      * class
459      */
460     static JSONValue toJson(int Depth=-1, T)(T value) if (is(T == class)) {
461         enum options = SerializationOptions().Depth(Depth);
462         return toJson!(options)(value);
463     }
464 
465 
466     /// ditto
467     static JSONValue toJson(SerializationOptions options, T)
468             (T value) if (is(T == class)) {
469                 
470         bool[size_t] serializationStates;
471         return toJsonImpl!(options)(value, serializationStates);
472     }
473 
474     /**
475      * Implements for class to json
476      */
477     private static JSONValue toJsonImpl(SerializationOptions options, T)
478             (T value, ref bool[size_t] serializationStates) if (is(T == class)) {
479         
480         debug(GEAR_DEBUG_MORE) {
481             Info("======== current type: class " ~ T.stringof);
482             log.trace("%s, T: %s",
483                 options, T.stringof);
484         }
485         static if(is(T : JsonSerializable)) {
486             // JsonSerializable first
487             return toJson!(JsonSerializable, IncludeMeta.no)(value);
488         } else {
489             JSONValue v = serializeObject!(options, T)(value, serializationStates);
490 
491             version(GEAR_DEBUG_MORE) {
492                 error(serializationStates);
493             }
494 
495             return v;
496         }
497     }
498 
499     deprecated("Using the other form of toJson(options) instead.")
500     static JSONValue toJson(T, TraverseBase traverseBase,
501             OnlyPublic onlyPublic = OnlyPublic.no, 
502             IncludeMeta includeMeta = IncludeMeta.no)
503             (T value) if (is(T == class)) {
504         enum options = SerializationOptions(OnlyPublic, TraverseBase, IncludeMeta);
505         bool[size_t] serializationStates;
506         return serializeObject!(options, T)(value, serializationStates);
507     }
508 
509 
510     deprecated("Using SerializeObject(SerializationOptions) instead.")
511     static JSONValue SerializeObject(OnlyPublic onlyPublic, TraverseBase traverseBase,
512         IncludeMeta includeMeta, T) (T value) if (is(T == class)) {
513 
514         enum options = SerializationOptions(OnlyPublic, TraverseBase, IncludeMeta);
515         bool[size_t] serializationStates;
516         return serializeObject!(options, T)(value, serializationStates);
517     }
518 
519     /**
520      * class object
521      */
522     static JSONValue SerializeObject(SerializationOptions options = SerializationOptions.Full, T)
523             (T value, ref bool[size_t] serializationStates) if (is(T == class)) {
524         import std.traits : isSomeFunction, isType;
525 
526         debug(GEAR_DEBUG_MORE) {
527             Info("======== current type: class " ~ T.stringof);
528             log.trace("%s, T: %s", options, T.stringof);
529             // log.trace("TraverseBase = %s, OnlyPublic = %s, IncludeMeta = %s, T: %s",
530             //     TraverseBase, OnlyPublic, IncludeMeta, T.stringof);
531         }
532 
533         if (value is null) {
534             version(GEAR_DEBUG) log.warn("value is null");
535             return JSONValue(null);
536         }
537 
538         size_t objHash = value.toHash() + hashOf(T.stringof);
539         auto itemPtr = objHash in serializationStates;
540         if(itemPtr !is null && *itemPtr) {
541             debug(GEAR_DEBUG_MORE) log.trace("%s serialized.", T.stringof);
542             return JSONValue(null);
543         }
544         
545         serializationStates[objHash] = true;
546 
547         auto result = JSONValue();
548         static if(options.IncludeMeta) {
549             result[MetaTypeName] = typeid(T).name;
550         }
551         // debug(GEAR_DEBUG_MORE) pragma(msg, "======== current type: class " ~ T.stringof);
552         
553         // super fields
554         static if(options.TraverseBase) {
555             alias baseClasses = BaseClassesTuple!T;
556             static if(baseClasses.length >= 1) {
557 
558                 alias BaseType = baseClasses[0];
559                 debug(GEAR_DEBUG_MORE) {
560                     log.trace("BaseType: %s", BaseType.stringof);
561                 }
562                 static if(!is(BaseType == Object)) {
563                     JSONValue superResult = serializeObject!(options, BaseType)(value, serializationStates);
564                     if(!superResult.isNull)
565                         result["super"] = superResult;
566                 }
567             }
568         }
569         
570         // current fields
571         static foreach (string member; FieldNameTuple!T) {
572             serializeMember!(member, options)(value, result, serializationStates);
573         }
574 
575         return result;
576     }
577 
578     /**
579      * struct
580      */
581     
582     static JSONValue toJson(SerializationOptions options = SerializationOptions(), T)(T value)
583             if (is(T == struct) && !is(T == SysTime)) {
584         bool[size_t] serializationStates;
585         return toJsonImpl!(options)(value, serializationStates);
586     }
587 
588     /**
589      * Implements for struct to json
590      */
591     static JSONValue toJsonImpl(SerializationOptions options = SerializationOptions(), T)(T value, 
592             ref bool[size_t] serializationStates) if (is(T == struct) && !is(T == SysTime)) {
593 
594         static if(is(T == JSONValue)) {
595             return value;
596         } else {
597             auto result = JSONValue();
598             // debug(GEAR_DEBUG_MORE) pragma(msg, "======== current type: struct " ~ T.stringof);
599             debug(GEAR_DEBUG_MORE) Info("======== current type: struct " ~ T.stringof);
600                 
601             static foreach (string member; FieldNameTuple!T) {
602                 serializeMember!(member, options)(value, result, serializationStates);
603             }
604 
605             return result;
606         }
607     }
608 
609     /**
610      * Object's memeber
611      */
612     private static void SerializeMember(string member, 
613             SerializationOptions options = SerializationOptions.Default, T)
614             (T obj, ref JSONValue result, ref bool[size_t] serializationStates) {
615 
616         // debug(GEAR_DEBUG_MORE) pragma(msg, "\tfield=" ~ member);
617 
618         alias currentMember = __traits(getMember, T, member);
619 
620         static if(options.OnlyPublic) {
621             static if (__traits(getProtection, currentMember) == "public") {
622                 enum canSerialize = true;
623             } else {
624                 enum canSerialize = false;
625             }
626         } else static if(hasUDA!(currentMember, Ignore) || hasUDA!(currentMember, JsonIgnore)) {
627             enum canSerialize = false;
628         } else {
629             enum canSerialize = true;
630         }
631         
632         debug(GEAR_DEBUG_MORE) {
633             log.trace("name: %s, %s", member, options);
634         }
635 
636         static if(canSerialize) {
637             alias memberType = typeof(currentMember);
638             debug(GEAR_DEBUG_MORE) log.info("memberType: %s in %s", memberType.stringof, T.stringof);
639 
640             static if(is(memberType == interface) && !is(memberType : JsonSerializable)) {
641                 version(GEAR_DEBUG) log.warn("skipped a interface member(not JsonSerializable): " ~ member);
642             } else {
643                 auto m = __traits(getMember, obj, member);
644 
645                 alias jsonPropertyUDAs = getUDAs!(currentMember, JsonProperty);
646                 static if(jsonPropertyUDAs.length > 0) {
647                     enum PropertyName = jsonPropertyUDAs[0].name;
648                     enum jsonKeyName = (PropertyName.length == 0) ? member : PropertyName;
649                 } else {
650                     enum jsonKeyName = member;
651                 }
652 
653                 auto json = serializeMember!(options)(m, serializationStates);
654 
655                 debug(GEAR_DEBUG_MORE) {
656                     log.trace("name: %s, value: %s", member, json.toString());
657                 }
658 
659                 bool canSetValue = true;
660                 if(json.isNull) {
661                     static if(options.IgnoreNull) {
662                         canSetValue = false;
663                     }
664                 }
665 
666                 if (canSetValue) {
667                         // Trace(result);
668                     if(!result.isNull) {
669                         auto jsonItemPtr = jsonKeyName in result;
670                         if(jsonItemPtr !is null) {
671                             version(GEAR_DEBUG) log.warn("overrided field: " ~ member);
672                         }
673                     }
674                     result[jsonKeyName] = json;
675                 }
676             }
677         } else {
678             debug(GEAR_DEBUG_MORE) log.trace("skipped member, name: %s", member);
679         }
680     }
681 
682     private static JSONValue SerializeMember(SerializationOptions options, T)(T m, 
683             ref bool[size_t] serializationStates) {
684         JSONValue json;
685         enum Depth = options.Depth;
686         static if(is(T == interface) && is(T : JsonSerializable)) {
687             static if(Depth == -1 || Depth > 0) { json = toJson!(JsonSerializable)(m);}
688         } else static if(is(T == SysTime)) {
689             json = toJson!SysTime(m);
690         // } else static if(isSomeString!T) {
691         //     json = toJson(m);
692         } else static if(is(T == class)) {
693             if(m !is null) {
694                 json = serializeObjectMember!(options)(m, serializationStates);
695             }
696         } else static if(is(T == struct)) {
697             json = serializeObjectMember!(options)(m, serializationStates);
698         } else static if(is(T : U[], U)) { 
699             if(m is null) {
700                 static if(!options.IgnoreNull) {
701                     static if(isSomeString!T) {
702                         json = toJson(m);
703                     } else {
704                         json = JSONValue[].init;
705                     }
706                 }
707             } else {
708                 static if (is(U == class) || is(U == struct) || is(U == interface)) {
709                     // class[] obj; struct[] obj;
710                     json = serializeObjectMember!(options)(m, serializationStates);
711                 } else {
712                     json = toJson(m);
713                 }
714             }
715         } else {
716             json = toJson(m);
717         }
718 
719         return json;
720         
721     }
722 
723     private static JSONValue SerializeObjectMember(SerializationOptions options = 
724             SerializationOptions.Default, T)(ref T m, ref bool[size_t] serializationStates) {
725         enum Depth = options.Depth;
726         static if(Depth > 0) {
727             enum SerializationOptions memeberOptions = options.Depth(options.Depth-1);
728             return toJsonImpl!(memeberOptions)(m, serializationStates);
729         } else static if(Depth == -1) {
730             return toJsonImpl!(options)(m, serializationStates);
731         } else {
732             return JSONValue.init;
733         }
734     }
735 
736     /**
737      * SysTime
738      */
739     static JSONValue toJson(T)(T value, bool asInteger=true) if(is(T == SysTime)) {
740         if(asInteger)
741             return JSONValue(value.stdTime()); // STD time
742         else 
743             return JSONValue(value.toString());
744     }
745 
746     /**
747      * JsonSerializable
748      */
749     static JSONValue toJson(T, IncludeMeta includeMeta = IncludeMeta.yes)
750                     (T value) if (is(T == interface) && is(T : JsonSerializable)) {
751 
752         debug(GEAR_DEBUG_MORE) {
753             if(value is null) {
754                 log.info("======== current type: interface = %s, Object = null", 
755                     T.stringof);
756             } else {
757                 log.info("======== current type: interface = %s, Object = %s", 
758                     T.stringof, typeid(cast(Object)value).name);
759             }
760         }
761         
762         if(value is null) {
763             return JSONValue(null);
764         }
765 
766         JSONValue v = value.jsonSerialize();
767         static if(IncludeMeta) {
768             auto itemPtr = MetaTypeName in v;
769             if(itemPtr is null)
770                 v[MetaTypeName] = typeid(cast(Object)value).name;
771         }
772         // TODO: Tasks pending completion -@zhangxueping at 2019-09-28T07:45:09+08:00
773         // Remove the MetaTypeName memeber
774         debug(GEAR_DEBUG_MORE) Trace(v.toString());
775         return v;
776     }
777 
778     /**
779      * Basic type
780      */
781     static JSONValue toJson(T)(T value) if (isBasicType!T) {
782         static if(is(T == double) || is(T == float)) {
783             import std.math : isNaN;
784             if(isNaN(value)) {
785                 log.warn("Uninitialized float/double value. It will be set to zero.");
786                 value = 0;
787             }
788         }
789         return JSONValue(value);
790     }
791 
792     /**
793      * T[]
794      */
795     static JSONValue toJson(SerializationOptions options = SerializationOptions.Default, T: U[], U)(T value) {
796         bool[size_t] serializationStates;
797         return toJsonImpl!(options)(value, serializationStates);
798     }
799 
800     private static JSONValue toJsonImpl(SerializationOptions options = SerializationOptions.Default, T: U[], U)(T value, 
801             ref bool[size_t] serializationStates) {
802 
803         static if(is(U == class)) { // class[]
804             if(value is null) {
805                 return JSONValue(JSONValue[].init);
806             } else {
807                 return JSONValue(value.map!(item => toJsonImpl!(options)(item, serializationStates))()
808                         .map!(json => json.isNull ? JSONValue(null) : json).array);
809             }
810         } else static if(is(U == struct)) { // struct[]
811             if(value is null) {
812                 return JSONValue(JSONValue[].init);
813             } else {
814                 static if(is(U == SysTime)) {
815                     return JSONValue(value.map!(item => toJson(item))()
816                             .map!(json => json.isNull ? JSONValue(null) : json).array);
817                 } else {
818                     return JSONValue(value.map!(item => toJsonImpl!(options)(item, serializationStates))()
819                             .map!(json => json.isNull ? JSONValue(null) : json).array);
820                 }
821             }
822         } else static if(is(U : S[], S)) { // S[][]
823             if(value is null) 
824                 return JSONValue(JSONValue[].init);
825 
826             JSONValue[] items;
827             foreach(S[] element; value) {
828                 static if(is(S == struct) || is(S == class)) {
829                     items ~= toJsonImpl(element, serializationStates);
830                 } else {
831                     items ~= toJson(element);
832                 }
833             }
834 
835             return JSONValue(items);
836         } else {
837             return JSONValue(value);
838         }
839     }
840 
841 
842     /**
843      * V[K]
844      */
845     static JSONValue toJson(SerializationOptions options = SerializationOptions.Default,
846             T : V[K], V, K)(T value) {
847         bool[size_t] serializationStates;
848         return toJsonImpl!(options)(value, serializationStates);
849     }
850 
851     private static JSONValue toJsonImpl(SerializationOptions options = SerializationOptions.Default,
852             T : V[K], V, K)(T value, ref bool[size_t] serializationStates) {
853         auto result = JSONValue();
854 
855         foreach (key; value.keys) {
856             static if(is(V == SysTime)) {
857                 auto json = toJson(value[key]);
858             } else static if(is(V == class) || is(V == struct) || is(V == interface)) {
859                 auto json = toJsonImpl!(options)(value[key], serializationStates);
860             } else {
861                 auto json = toJson(value[key]);
862             }
863             result[key.to!string] = json.isNull ? JSONValue(null) : json;
864         }
865 
866         return result;
867     }
868 
869     deprecated("Using toObject instead.")
870     alias fromJson = toObject;
871 }
872 
873 
874 alias toJson = JsonSerializer.toJson;
875 alias toObject = JsonSerializer.toObject;
876 
877 
878 deprecated("Using toObject instead.")
879 alias fromJson = JsonSerializer.toObject;