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 import hunt.util.Common;
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, TraverseBase traverseBase = TraverseBase.yes, bool canThrow = false)
74             (string json, T defaultValue = T.init) if (is(T == class)) {
75         return toObject!(T, traverseBase, canThrow)(parseJSON(json));
76     }
77 
78     static T toObject(T, bool canThrow = false)
79             (string json, T defaultValue = T.init) if (!is(T == class)) {
80         return toObject!(T, canThrow)(parseJSON(json));
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, TraverseBase traverseBase = TraverseBase.yes, bool canThrow = false)
87             (auto ref const(JSONValue) json, T defaultValue = T.init) 
88             if (is(T == class) && __traits(compiles, new T())) { // is(typeof(new T()))
89 
90         if (json.type() != JSONType.object) {
91             return handleException!(T, canThrow)(json, "wrong object type", defaultValue);
92         }
93 
94         auto result = new T();
95 
96         static if(is(T : JsonSerializable)) {
97             result.jsonDeserialize(json);
98         } else {
99             try {
100                 deserializeObject!(T, traverseBase)(result, json);
101             } catch (JSONException e) {
102                 return handleException!(T, canThrow)(json, e.msg, defaultValue);
103             }
104         }
105 
106         return result;
107     }
108 
109     /**
110      * 
111      */
112     static T toObject(T, bool canThrow = false)(
113             auto ref const(JSONValue) json, T defaultValue = T.init) 
114             if (is(T == struct) && !is(T == SysTime)) {
115 
116         JSONType jt = json.type();
117 
118         if (jt != JSONType.object) {
119             return handleException!(T, canThrow)(json, "wrong object type", defaultValue);
120         }
121 
122         auto result = T();
123 
124         try {
125             static foreach (string member; FieldNameTuple!T) {
126                 deserializeMember!(member, TraverseBase.no)(result, json);
127             }
128         } catch (JSONException e) {
129             return handleException!(T, canThrow)(json, e.msg, defaultValue);
130         }
131 
132         return result;
133     }
134 
135 
136     static void deserializeObject(T)(ref T target, auto ref const(JSONValue) json)
137          if(is(T == struct)) {
138 
139         static foreach (string member; FieldNameTuple!T) {
140             // current fields
141             deserializeMember!(member, TraverseBase.no)(target, json);
142         }
143     }
144 
145     /**
146     */
147     static void deserializeObject(T, TraverseBase traverseBase = TraverseBase.yes)
148             (T target, auto ref const(JSONValue) json) if(is(T == class)) {
149 
150         static foreach (string member; FieldNameTuple!T) {
151             // current fields
152             deserializeMember!(member, traverseBase)(target, json);
153         }
154 
155         // super fields
156         static if(traverseBase) {
157             alias baseClasses = BaseClassesTuple!T;
158             alias BaseType = baseClasses[0];
159 
160             static if(baseClasses.length >= 1 && !is(BaseType == Object)) {
161                 debug(HUNT_DEBUG_MORE) {
162                     infof("TODO: deserializing fields in base %s for %s", BaseType.stringof, T.stringof);
163                 }
164                 auto jsonItemPtr = "super" in json;
165                 if(jsonItemPtr !is null) {
166                     deserializeObject!(BaseType, traverseBase)(target, *jsonItemPtr);
167                 }
168             }
169         }
170     }
171 
172     private static void deserializeMember(string member, TraverseBase traverseBase = TraverseBase.yes, T)
173             (ref T target, auto ref const(JSONValue) json) {
174         
175         alias currentMember = __traits(getMember, T, member);
176         alias memberType = typeof(currentMember);
177         debug(HUNT_DEBUG_MORE) {
178             infof("deserializing member: %s %s", memberType.stringof, member);
179         }
180 
181         static if(hasUDA!(currentMember, Ignore) || hasUDA!(currentMember, JsonIgnore)) { 
182             version(HUNT_DEBUG) {
183                 infof("Ignore a member: %s %s", memberType.stringof, member);
184             }                
185         } else {
186             static if(is(memberType == interface) && !is(memberType : JsonSerializable)) {
187                 version(HUNT_DEBUG) warning("skipped a interface member (not XmlSerializable): " ~ member);
188             } else {
189 
190                 alias jsonPropertyUDAs = getUDAs!(currentMember, JsonProperty);
191                 static if(jsonPropertyUDAs.length > 0) {
192                     enum PropertyName = jsonPropertyUDAs[0].name;
193                     enum jsonKeyName = (PropertyName.length == 0) ? member : PropertyName;
194                 } else {
195                     enum jsonKeyName = member;
196                 }
197 
198                 auto jsonItemPtr = jsonKeyName in json;
199                 if(jsonItemPtr is null) {
200                     version(HUNT_DEBUG) {
201                         if(jsonKeyName != member)
202                             warningf("No data available for member: %s as %s", member, jsonKeyName);
203                         else
204                             warningf("No data available for member: %s", member);
205                     }
206                 } else {
207                     debug(HUNT_DEBUG_MORE) tracef("available data: %s = %s", member, jsonItemPtr.toString());
208                     static if(is(memberType == class)) {
209                         __traits(getMember, target, member) = toObject!(memberType)(*jsonItemPtr);
210                     } else {
211                         __traits(getMember, target, member) = toObject!(memberType, false)(*jsonItemPtr);
212                     }
213                 }
214             }   
215         }
216     }
217 
218     static T toObject(T, bool canThrow = false)(
219             auto ref const(JSONValue) json, 
220             T defaultValue = T.init) 
221             if(is(T == interface) && is(T : JsonSerializable)) {
222 
223         auto jsonItemPtr = MetaTypeName in json;
224         if(jsonItemPtr is null) {
225             warningf("Can't find 'type' item for interface %s", T.stringof);
226             return T.init;
227         }
228         string typeId = jsonItemPtr.str;
229         T t = cast(T) Object.factory(typeId);
230         if(t is null) {
231             warningf("Can't create instance for %s", T.stringof);
232         }
233         t.jsonDeserialize(json);
234         return t;
235     
236     }
237 
238     static T toObject(T, bool canThrow = false)(
239             auto ref const(JSONValue) json, 
240             T defaultValue = T.init) 
241             if(is(T == SysTime)) {
242   
243         JSONType jt = json.type();
244         if(jt == JSONType..string) {
245             return SysTime.fromSimpleString(json.str);
246         } else if(jt == JSONType.integer) {
247             return SysTime(json.integer);  // STD time
248         } else {
249             return handleException!(T, canThrow)(json, "wrong SysTime type", defaultValue);
250         }
251     }
252 
253     // static N toObject(N : Nullable!T, T, bool canThrow = false)(auto ref const(JSONValue) json) {
254 
255     //     return (json.type == JSONType.null_) ? N() : toObject!T(json).nullable;
256     // }
257 
258     static T toObject(T : JSONValue, bool canThrow = false)(auto ref const(JSONValue) json) {
259         import std.typecons : nullable;
260         return json.nullable.get();
261     }
262 
263     static T toObject(T, bool canThrow = false)
264             (auto ref const(JSONValue) json, T defaultValue = T.init) 
265             if (isNumeric!T || isSomeChar!T) {
266 
267         switch (json.type) {
268         case JSONType.null_, JSONType.false_:
269             return 0.to!T;
270 
271         case JSONType.true_:
272             return 1.to!T;
273 
274         case JSONType.float_:
275             return json.floating.to!T;
276 
277         case JSONType.integer:
278             return json.integer.to!T;
279 
280         case JSONType.uinteger:
281             return json.uinteger.to!T;
282 
283         case JSONType..string:
284             try {
285                 return json.str.to!T;
286             } catch(Exception ex) {
287                 return handleException!(T, canThrow)(json, ex.msg, defaultValue);
288             }
289 
290         default:
291             return handleException!(T, canThrow)(json, "", defaultValue);
292         }
293     }
294 
295     static T handleException(T, bool canThrow = false) (auto ref const(JSONValue) json, 
296         string message, T defaultValue = T.init) {
297         static if (canThrow) {
298             throw new JSONException(json.toString() ~ " is not a " ~ T.stringof ~ " type");
299         } else {
300         version (HUNT_DEBUG)
301             warningf(" %s is not a %s type. Using the defaults instead! \n Exception: %s",
302                 json.toString(), T.stringof, message);
303             return defaultValue;
304         }
305     }
306 
307     static T toObject(T, bool canThrow = false)
308             (auto ref const(JSONValue) json) if (isBoolean!T) {
309 
310         switch (json.type) {
311         case JSONType.null_, JSONType.false_:
312             return false;
313 
314         case JSONType.float_:
315             return json.floating != 0;
316 
317         case JSONType.integer:
318             return json.integer != 0;
319 
320         case JSONType.uinteger:
321             return json.uinteger != 0;
322 
323         case JSONType..string:
324             return json.str.length > 0;
325 
326         default:
327             return true;
328         }
329     }
330 
331     static T toObject(T, bool canThrow = false)(auto ref const(JSONValue) json, T defaultValue = T.init)
332             if (isSomeString!T || is(T : string) || is(T : wstring) || is(T : dstring)) {
333 
334         static if (is(T == enum)) {
335             foreach (member; __traits(allMembers, T)) {
336                 auto m = __traits(getMember, T, member);
337 
338                 if (json.str == m) {
339                     return m;
340                 }
341             }
342             return handleException!(T, canThrow)(json, 
343                 " is not a member of " ~ typeid(T).toString(), defaultValue);
344         } else {
345             return (json.type == JSONType..string ? json.str : json.toString()).to!T;
346         }
347     }
348 
349     static T toObject(T : U[], bool canThrow = false, U)
350             (auto ref const(JSONValue) json, 
351             T defaultValue = T.init)
352             if (isArray!T && !isSomeString!T && !is(T : string) && !is(T
353                 : wstring) && !is(T : dstring)) {
354 
355         switch (json.type) {
356         case JSONType.null_:
357             return [];
358 
359         case JSONType.false_:
360             return [toObject!U(JSONValue(false))];
361 
362         case JSONType.true_:
363             return [toObject!U(JSONValue(true))];
364 
365         case JSONType.array:
366             return json.array
367                 .map!(value => toObject!U(value))
368                 .array
369                 .to!T;
370 
371         case JSONType.object:
372             return handleException!(T, canThrow)(json, "", defaultValue);
373 
374         default:
375             return [toObject!U(json)];
376         }
377     }
378 
379     static T toObject(T : U[K], bool canThrow = false, U, K)(
380             auto ref const(JSONValue) json, T defaultValue = T.init) 
381             if (isAssociativeArray!T) {
382         
383         U[K] result;
384 
385         switch (json.type) {
386         case JSONType.null_:
387             return result;
388 
389         case JSONType.object:
390             foreach (key, value; json.object) {
391                 result[key.to!K] = toObject!U(value);
392             }
393 
394             break;
395 
396         case JSONType.array:
397             foreach (key, value; json.array) {
398                 result[key.to!K] = toObject!U(value);
399             }
400 
401             break;
402 
403         default:
404             return handleException!(T, canThrow)(json, "", defaultValue);
405         }
406 
407         return result;
408     }
409 
410 
411     /* -------------------------------------------------------------------------- */
412     /*                                   toJson                                   */
413     /* -------------------------------------------------------------------------- */
414 
415     /**
416      * class
417      */
418     static JSONValue toJson(int depth=-1, T)(T value) if (is(T == class)) {
419         enum options = SerializationOptions().depth(depth);
420         return toJson!(options)(value);
421     }
422 
423 
424     /// ditto
425     static JSONValue toJson(SerializationOptions options, T)
426             (T value) if (is(T == class)) {
427         
428         debug(HUNT_DEBUG_MORE) {
429             info("======== current type: class " ~ T.stringof);
430             tracef("%s, T: %s",
431                 options, T.stringof);
432         }
433         static if(is(T : JsonSerializable)) {
434             // JsonSerializable first
435             return toJson!(JsonSerializable, IncludeMeta.no)(value);
436         } else {
437             return serializeObject!(options, T)(value);
438         }
439     }
440 
441     deprecated("Using the other form of toJson(options) instead.")
442     static JSONValue toJson(T, TraverseBase traverseBase,
443             OnlyPublic onlyPublic = OnlyPublic.no, 
444             IncludeMeta includeMeta = IncludeMeta.no)
445             (T value) if (is(T == class)) {
446         enum options = SerializationOptions(onlyPublic, traverseBase, includeMeta);
447         return serializeObject!(options, T)(value);
448     }
449 
450 
451     deprecated("Using serializeObject(SerializationOptions) instead.")
452     static JSONValue serializeObject(OnlyPublic onlyPublic, TraverseBase traverseBase,
453         IncludeMeta includeMeta, T) (T value) if (is(T == class)) {
454 
455         enum options = SerializationOptions(onlyPublic, traverseBase, includeMeta);
456         return serializeObject!(options, T)(value);
457     }
458 
459     /**
460      * class object
461      */
462     static JSONValue serializeObject(SerializationOptions options = SerializationOptions.Full, T)
463             (T value) if (is(T == class)) {
464         import std.traits : isSomeFunction, isType;
465 
466         debug(HUNT_DEBUG_MORE) {
467             info("======== current type: class " ~ T.stringof);
468             tracef("%s, T: %s", options, T.stringof);
469             // tracef("traverseBase = %s, onlyPublic = %s, includeMeta = %s, T: %s",
470             //     traverseBase, onlyPublic, includeMeta, T.stringof);
471         }
472 
473         if (value is null) {
474             version(HUNT_DEBUG) warning("value is null");
475             return JSONValue(null);
476         }
477 
478         auto result = JSONValue();
479         static if(options.includeMeta) {
480             result[MetaTypeName] = typeid(T).name;
481         }
482         // debug(HUNT_DEBUG_MORE) pragma(msg, "======== current type: class " ~ T.stringof);
483         
484         // super fields
485         static if(options.traverseBase) {
486             alias baseClasses = BaseClassesTuple!T;
487             static if(baseClasses.length >= 1) {
488 
489                 alias BaseType = baseClasses[0];
490                 debug(HUNT_DEBUG_MORE) {
491                     tracef("BaseType: %s", BaseType.stringof);
492                 }
493                 static if(!is(BaseType == Object)) {
494                     JSONValue superResult = serializeObject!(options, BaseType)(value);
495                     if(!superResult.isNull)
496                         result["super"] = superResult;
497                 }
498             }
499         }
500         
501         // current fields
502 		static foreach (string member; FieldNameTuple!T) {
503             serializeMember!(member, options)(value, result);
504         }
505 
506         return result;
507     }
508 
509     /**
510      * struct
511      */
512     static JSONValue toJson(SerializationOptions options = SerializationOptions(), T)(T value)
513             if (is(T == struct) && !is(T == SysTime)) {
514 
515         static if(is(T == JSONValue)) {
516             return value;
517         } else {
518             auto result = JSONValue();
519             // debug(HUNT_DEBUG_MORE) pragma(msg, "======== current type: struct " ~ T.stringof);
520             debug(HUNT_DEBUG_MORE) info("======== current type: struct " ~ T.stringof);
521                 
522             static foreach (string member; FieldNameTuple!T) {
523                 serializeMember!(member, options)(value, result);
524             }
525 
526             return result;
527         }
528     }
529 
530     /**
531      * Object's memeber
532      */
533     private static void serializeMember(string member, 
534             SerializationOptions options = SerializationOptions.Default, T)
535             (T obj, ref JSONValue result) {
536 
537         // debug(HUNT_DEBUG_MORE) pragma(msg, "\tfield=" ~ member);
538 
539         alias currentMember = __traits(getMember, T, member);
540 
541         static if(options.onlyPublic) {
542             static if (__traits(getProtection, currentMember) == "public") {
543                 enum canSerialize = true;
544             } else {
545                 enum canSerialize = false;
546             }
547         } else static if(hasUDA!(currentMember, Ignore) || hasUDA!(currentMember, JsonIgnore)) {
548             enum canSerialize = false;
549         } else {
550             enum canSerialize = true;
551         }
552         
553         debug(HUNT_DEBUG_MORE) {
554             tracef("name: %s, %s", member, options);
555         }
556 
557         static if(canSerialize) {
558             alias memberType = typeof(currentMember);
559             debug(HUNT_DEBUG_MORE) infof("memberType: %s in %s", memberType.stringof, T.stringof);
560 
561             static if(is(memberType == interface) && !is(memberType : JsonSerializable)) {
562                 version(HUNT_DEBUG) warning("skipped a interface member(not JsonSerializable): " ~ member);
563             } else {
564                 auto m = __traits(getMember, obj, member);
565 
566                 alias jsonPropertyUDAs = getUDAs!(currentMember, JsonProperty);
567                 static if(jsonPropertyUDAs.length > 0) {
568                     enum PropertyName = jsonPropertyUDAs[0].name;
569                     enum jsonKeyName = (PropertyName.length == 0) ? member : PropertyName;
570                 } else {
571                     enum jsonKeyName = member;
572                 }
573 
574                 auto json = serializeMember!(options)(m);
575 
576                 debug(HUNT_DEBUG_MORE) {
577                     tracef("name: %s, value: %s", member, json.toString());
578                 }
579 
580                 bool canSetValue = true;
581                 if(json.isNull) {
582                     static if(options.ignoreNull) {
583                         canSetValue = false;
584                     }
585                 }
586 
587                 if (canSetValue) {
588                         // trace(result);
589                     if(!result.isNull) {
590                         auto jsonItemPtr = jsonKeyName in result;
591                         if(jsonItemPtr !is null) {
592                             version(HUNT_DEBUG) warning("overrided field: " ~ member);
593                         }
594                     }
595                     result[jsonKeyName] = json;
596                 }
597             }
598         } else {
599             debug(HUNT_DEBUG_MORE) tracef("skipped member, name: %s", member);
600         }
601     }
602 
603     private static JSONValue serializeMember(SerializationOptions options, T)(T m) {
604         JSONValue json;
605         enum depth = options.depth;
606         static if(is(T == interface) && is(T : JsonSerializable)) {
607             static if(depth == -1 || depth > 0) { json = toJson!(JsonSerializable)(m);}
608         } else static if(is(T == SysTime)) {
609             json = toJson!SysTime(m);
610         // } else static if(isSomeString!T) {
611         //     json = toJson(m);
612         } else static if(is(T == class)) {
613             if(m !is null) {
614                 json = serializeObjectMember!(options)(m);
615             }
616         } else static if(is(T == struct)) {
617             json = serializeObjectMember!(options)(m);
618         } else static if(is(T : U[], U)) { 
619             if(m is null) {
620                 static if(!options.ignoreNull) {
621                     static if(isSomeString!T) {
622                         json = toJson(m);
623                     } else {
624                         json = JSONValue[].init;
625                     }
626                 }
627             } else {
628                 static if (is(U == class) || is(U == struct) || is(U == interface)) {
629                     // class[] obj; struct[] obj;
630                     json = serializeObjectMember!(options)(m);
631                 } else {
632                     json = toJson(m);
633                 }
634             }
635         } else {
636             json = toJson(m);
637         }
638 
639         return json;
640         
641     }
642 
643     private static JSONValue serializeObjectMember(SerializationOptions options = 
644             SerializationOptions.Default, T)(ref T m) {
645         enum depth = options.depth;
646         static if(depth > 0) {
647             enum SerializationOptions memeberOptions = options.depth(options.depth-1);
648             return toJson!(memeberOptions)(m);
649         } else static if(depth == -1) {
650             return toJson!(options)(m);
651         } else {
652             return JSONValue.init;
653         }
654     }
655 
656     /**
657      * SysTime
658      */
659     static JSONValue toJson(T)(T value, bool asInteger=true) if(is(T == SysTime)) {
660         if(asInteger)
661             return JSONValue(value.stdTime()); // STD time
662         else 
663             return JSONValue(value.toString());
664     }
665 
666     /**
667      * JsonSerializable
668      */
669     static JSONValue toJson(T, IncludeMeta includeMeta = IncludeMeta.yes)
670                     (T value) if (is(T == interface) && is(T : JsonSerializable)) {
671         debug(HUNT_DEBUG_MORE) {
672             infof("======== current type: interface = %s, Object = %s", 
673                 T.stringof, typeid(cast(Object)value).name);
674         }
675 
676         JSONValue v = value.jsonSerialize();
677         static if(includeMeta) {
678             auto itemPtr = MetaTypeName in v;
679             if(itemPtr is null)
680                 v[MetaTypeName] = typeid(cast(Object)value).name;
681         }
682         // TODO: Tasks pending completion -@zhangxueping at 2019-09-28T07:45:09+08:00
683         // remove the MetaTypeName memeber
684         debug(HUNT_DEBUG_MORE) trace(v.toString());
685         return v;
686     }
687 
688     /**
689      * Basic type
690      */
691     static JSONValue toJson(T)(T value) if (isBasicType!T) {
692         return JSONValue(value);
693     }
694 
695     /**
696      * string[]
697      */
698     static JSONValue toJson(T)(T value)
699             if (is(T : U[], U) && (isBasicType!U || isSomeString!U)) {
700         return JSONValue(value);
701     }
702 
703     /**
704      * class[]
705      */
706     static JSONValue toJson(SerializationOptions options = SerializationOptions.Normal, 
707             T : U[], U) (T value) if(is(T : U[], U) && is(U == class)) {
708         if(value is null) {
709             return JSONValue(JSONValue[].init);
710         } else {
711             return JSONValue(value.map!(item => toJson!(options)(item))()
712                     .map!(json => json.isNull ? JSONValue(null) : json).array);
713         }
714     }
715     
716     /**
717      * struct[]
718      */
719     static JSONValue toJson(SerializationOptions options = SerializationOptions.Normal,
720             T : U[], U)(T value) if(is(U == struct)) {
721         if(value is null) {
722             return JSONValue(JSONValue[].init);
723         } else {
724             static if(is(U == SysTime)) {
725                 return JSONValue(value.map!(item => toJson(item))()
726                         .map!(json => json.isNull ? JSONValue(null) : json).array);
727             } else {
728                 return JSONValue(value.map!(item => toJson!(options)(item))()
729                         .map!(json => json.isNull ? JSONValue(null) : json).array);
730             }
731         }
732     }
733 
734     /**
735      * V[K]
736      */
737     static JSONValue toJson(SerializationOptions options = SerializationOptions.Normal,
738             T : V[K], V, K)(T value) {
739         auto result = JSONValue();
740 
741         foreach (key; value.keys) {
742             static if(is(V == SysTime)) {
743                 auto json = toJson(value[key]);
744             } else static if(is(V == class) || is(V == struct) || is(V == interface)) {
745                 auto json = toJson!(options)(value[key]);
746             } else {
747                 auto json = toJson(value[key]);
748             }
749             result[key.to!string] = json.isNull ? JSONValue(null) : json;
750         }
751 
752         return result;
753     }
754 
755 
756     deprecated("Using toObject instead.")
757     alias fromJson = toObject;
758 }
759 
760 
761 alias toJson = JsonSerializer.toJson;
762 alias toObject = JsonSerializer.toObject;
763 
764 
765 deprecated("Using toObject instead.")
766 alias fromJson = JsonSerializer.toObject;