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;