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