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