1 module dutils.data.json; 2 3 void populateFromJSON(T)(ref T object, ref JSON data) { 4 populateFromJSON(object, data, ""); 5 } 6 7 void populateFromJSON(T)(ref T object, ref JSON data, string pathPrefix = "") { 8 import std.conv : to; 9 import std.datetime.systime : SysTime; 10 import std.uuid : UUID; 11 import std.traits : isSomeFunction, isNumeric, isSomeString, isBoolean; //, isArray, isBuiltinType; 12 import dutils.validation.validate : validate, ValidationError, ValidationErrors; 13 import dutils.validation.constraints : ValidationErrorTypes; 14 15 ValidationError[] errors; 16 17 static foreach (member; __traits(derivedMembers, T)) { 18 static if (!isSomeFunction!(__traits(getMember, T, member))) { 19 if (data[member].type != JSON.Type.undefined) { 20 auto JSONValue = data[member]; 21 auto JSONType = JSONValue.type; 22 const path = pathPrefix == "" ? member : pathPrefix ~ "." ~ member; 23 24 try { 25 alias structType = typeof(__traits(getMember, T, member)); 26 27 if (isNumeric!(structType) && (JSONType == JSON.Type.int_ 28 || JSONType == JSON.Type.bigInt || JSONType == JSON.Type.float_)) { 29 __traits(getMember, object, member) = JSONValue.to!(structType); 30 } else if ((isSomeString!(structType) || is(structType == UUID) 31 || is(structType == SysTime)) && JSONType == JSON.Type..string) { 32 __traits(getMember, object, member) = JSONValue.to!(structType); 33 } else if (isBoolean!(structType)) { 34 __traits(getMember, object, member) = JSONValue.to!(structType); 35 } /* 36 // TODO: support array recursion 37 } else if (isArray!(structType)) { 38 foreach (child; JSONValue) { 39 __traits(getMember, object, member) ~= 40 } 41 // TODO: support nested calls 42 } else if (!isBuiltinType!(structType)) { 43 fromJSON( __traits(getMember, object, member), JSONValue, path); 44 } 45 */ 46 else { 47 throw new Exception("Unsupported type"); 48 } 49 } catch (Exception error) { 50 errors ~= ValidationError(path, ValidationErrorTypes.type, 51 "Value must be convertable to type " ~ typeof(__traits(getMember, 52 T, member)).stringof ~ " but got " ~ data[member].type.to!string); 53 } 54 } 55 } 56 } 57 58 try { 59 validate(object); 60 } catch (ValidationErrors validation) { 61 errors ~= validation.errors; 62 } 63 64 if (errors.length > 0) { 65 throw new ValidationErrors(errors); 66 } 67 } 68 69 /** 70 * populateFromJSON - ensure that deserialization works with valid JSON data 71 */ 72 unittest { 73 import dutils.validation.constraints : ValidateMinimumLength, 74 ValidateMaximumLength, ValidateMinimum, ValidateEmail, ValidateRequired; 75 76 struct Person { 77 @ValidateMinimumLength(2) 78 @ValidateMaximumLength(100) 79 string name; 80 81 @ValidateMinimum!double(20) double height; 82 83 @ValidateEmail() 84 @ValidateRequired() 85 string email; 86 87 @ValidateRequired() 88 bool member; 89 } 90 91 auto data = JSON([ 92 "does not exists": JSON(true), 93 "name": JSON("Anna"), 94 "height": JSON(170.1), 95 "email": JSON("anna@example.com"), 96 "member": JSON(true) 97 ]); 98 99 Person person; 100 populateFromJSON(person, data); 101 102 assert(person.name == "Anna", "expected name Anna"); 103 assert(person.height == 170.1, "expected height 170.1"); 104 assert(person.email == "anna@example.com", "expected email anna@example.com"); 105 assert(person.member == true, "expected member true"); 106 } 107 108 /** 109 * populateFromJSON - ensure that validation errors are thrown with invalid JSON data 110 */ 111 unittest { 112 import std.conv : to; 113 import dutils.validation.validate : ValidationError, ValidationErrors; 114 import dutils.validation.constraints : ValidationErrorTypes, ValidateMinimumLength, 115 ValidateMaximumLength, ValidateMinimum, ValidateEmail, ValidateRequired; 116 117 struct Person { 118 @ValidateMinimumLength(2) 119 @ValidateMaximumLength(100) 120 string name; 121 122 @ValidateMinimum!float(20) float height; 123 124 @ValidateEmail() 125 @ValidateRequired() 126 string email; 127 128 @ValidateRequired() 129 bool member; 130 } 131 132 auto data = JSON([ 133 "does not exists": JSON(true), 134 "name": JSON("Anna"), 135 "height": JSON("not a number") 136 ]); 137 138 Person person; 139 140 auto catched = false; 141 try { 142 populateFromJSON(person, data); 143 } catch (ValidationErrors validation) { 144 catched = true; 145 assert(validation.errors.length == 4, 146 "expected 4 errors, got " ~ validation.errors.length.to!string 147 ~ " with message: " ~ validation.msg); 148 assert(validation.errors[0].type == "type", "expected minimumLength error"); 149 assert(validation.errors[0].path == "height", "expected error path to be height"); 150 assert(validation.errors[1].type == "minimum", "expected minimum error"); 151 assert(validation.errors[1].path == "height", "expected error path to be height"); 152 assert(validation.errors[2].type == "required", "expected required error"); 153 assert(validation.errors[2].path == "email", "expected error path to be email"); 154 assert(validation.errors[3].type == "required", "expected required error"); 155 assert(validation.errors[3].path == "member", "expected error path to be member"); 156 } 157 158 assert(catched == true, "did not catch the expected errors"); 159 } 160 161 /** 162 * populateFromJSON - should populate 163 */ 164 unittest { 165 import dutils.validation.constraints : ValidateRequired, ValidateEmail; 166 167 struct Email { 168 @ValidateRequired() 169 @ValidateEmail() 170 string to; 171 172 @ValidateEmail() 173 string from; 174 175 string subject; 176 177 @ValidateRequired() 178 string body; 179 } 180 181 auto data = JSON([ 182 "does not exists": JSON(true), 183 "to": JSON("anna@example.com"), 184 "body": JSON("Some text") 185 ]); 186 187 Email email; 188 populateFromJSON(email, data); 189 190 assert(email.to == "anna@example.com", "expected to to be anna@example.com"); 191 assert(email.from == "", "expected from to be \"\""); 192 assert(email.subject == "", "expected from to be \"\""); 193 assert(email..body == "Some text", "expected from to be \"Some text\""); 194 } 195 196 /** 197 JSON serialization and value handling. 198 199 This module provides the JSON struct for reading, writing and manipulating 200 JSON values. De(serialization) of arbitrary D types is also supported and 201 is recommended for handling JSON in performance sensitive applications. 202 203 Copyright: © 2012-2015 Sönke Ludwig 204 License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file. 205 Authors: Sönke Ludwig 206 */ 207 208 import dutils.data.utils.serialization; 209 210 /// 211 @safe unittest { 212 void manipulateJSON(JSON j) { 213 import std.stdio; 214 215 // retrieving the values is done using get() 216 assert(j["name"].get!string == "Example"); 217 assert(j["id"].get!int == 1); 218 219 // semantic conversions can be done using to() 220 assert(j["id"].to!string == "1"); 221 222 // prints: 223 // name: "Example" 224 // id: 1 225 foreach (key, value; j.byKeyValue) 226 writefln("%s: %s", key, value); 227 228 // print out as JSON: {"name": "Example", "id": 1} 229 writefln("JSON: %s", j.toString()); 230 } 231 } 232 233 /// Constructing `JSON` objects 234 @safe unittest { 235 // construct a JSON object {"field1": "foo", "field2": 42, "field3": true} 236 237 // using the constructor 238 JSON j1 = JSON([ 239 "field1": JSON("foo"), 240 "field2": JSON(42), 241 "field3": JSON(true) 242 ]); 243 244 // using piecewise construction 245 JSON j2 = JSON.emptyObject; 246 j2["field1"] = "foo"; 247 j2["field2"] = 42.0; 248 j2["field3"] = true; 249 250 // using serialization 251 struct S { 252 string field1; 253 double field2; 254 bool field3; 255 } 256 257 JSON j3 = S("foo", 42, true).serializeToJSON(); 258 259 // using serialization, converting directly to a JSON string 260 string j4 = S("foo", 32, true).serializeToJSONString(); 261 } 262 263 public import std.json : JSONException; 264 import std.algorithm; 265 import std.array; 266 import std.bigint; 267 import std.conv; 268 import std.datetime; 269 import std.exception; 270 import std.format; 271 272 static if (__VERSION__ >= 2082) { 273 import std.json : JSONValue, JSONType; 274 } else { 275 import std.json : JSONValue, JSON_TYPE; 276 277 private enum JSONType : byte { 278 null_ = JSON_TYPE.NULL, 279 string = JSON_TYPE.STRING, 280 integer = JSON_TYPE.INTEGER, 281 uinteger = JSON_TYPE.UINTEGER, 282 float_ = JSON_TYPE.FLOAT, 283 array = JSON_TYPE.ARRAY, 284 object = JSON_TYPE.OBJECT, 285 true_ = JSON_TYPE.TRUE, 286 false_ = JSON_TYPE.FALSE, 287 } 288 } 289 import std.range; 290 import std.string; 291 import std.traits; 292 import std.typecons : Tuple; 293 import std.uuid; 294 295 /******************************************************************************/ 296 /* public types */ 297 /******************************************************************************/ 298 299 /** 300 Represents a single JSON value. 301 302 JSON values can have one of the types defined in the JSON.Type enum. They 303 behave mostly like values in ECMA script in the way that you can 304 transparently perform operations on them. However, strict typechecking is 305 done, so that operations between differently typed JSON values will throw 306 a JSONException. Additionally, an explicit cast or using get!() or to!() is 307 required to convert a JSON value to the corresponding static D type. 308 */ 309 align(8) // ensures that pointers stay on 64-bit boundaries on x64 so that they get scanned by the GC 310 struct JSON { 311 @safe: 312 313 static assert(!hasElaborateDestructor!BigInt && !hasElaborateCopyConstructor!BigInt, 314 "struct JSON is missing required ~this and/or this(this) members for BigInt."); 315 316 private { 317 // putting all fields in a union results in many false pointers leading to 318 // memory leaks and, worse, std.algorithm.swap triggering an assertion 319 // because of internal pointers. This crude workaround seems to fix 320 // the issues. 321 enum m_size = max((BigInt.sizeof + (void*).sizeof), 2); 322 // NOTE : DMD 2.067.1 doesn't seem to init void[] correctly on its own. 323 // Explicity initializing it works around this issue. Using a void[] 324 // array here to guarantee that it's scanned by the GC. 325 void[m_size] m_data = (void[m_size]).init; 326 327 static assert(m_data.offsetof == 0, "m_data must be the first struct member."); 328 static assert(BigInt.alignof <= 8, 329 "JSON struct alignment of 8 isn't sufficient to store BigInt."); 330 331 ref inout(T) getDataAs(T)() inout @trusted { 332 static assert(T.sizeof <= m_data.sizeof); 333 return (cast(inout(T)[1]) m_data[0 .. T.sizeof])[0]; 334 } 335 336 @property ref inout(BigInt) m_bigInt() inout { 337 return getDataAs!BigInt(); 338 } 339 340 @property ref inout(long) m_int() inout { 341 return getDataAs!long(); 342 } 343 344 @property ref inout(double) m_float() inout { 345 return getDataAs!double(); 346 } 347 348 @property ref inout(bool) m_bool() inout { 349 return getDataAs!bool(); 350 } 351 352 @property ref inout(string) m_string() inout { 353 return getDataAs!string(); 354 } 355 356 @property ref inout(JSON[string]) m_object() inout { 357 return getDataAs!(JSON[string])(); 358 } 359 360 @property ref inout(JSON[]) m_array() inout { 361 return getDataAs!(JSON[])(); 362 } 363 364 Type m_type = Type.undefined; 365 366 version (VibeJSONFieldNames) { 367 string m_name; 368 } 369 } 370 371 /** Represents the run time type of a JSON value. 372 */ 373 enum Type { 374 undefined, /// A non-existent value in a JSON object 375 null_, /// Null value 376 bool_, /// Boolean value 377 int_, /// 64-bit integer value 378 bigInt, /// BigInt values 379 float_, /// 64-bit floating point value 380 string, /// UTF-8 string 381 array, /// Array of JSON values 382 object, /// JSON object aka. dictionary from string to JSON 383 } 384 385 /// New JSON value of Type.Undefined 386 static @property JSON undefined() { 387 return JSON(); 388 } 389 390 /// New JSON value of Type.Object 391 static @property JSON emptyObject() { 392 return JSON(cast(JSON[string]) null); 393 } 394 395 /// New JSON value of Type.Array 396 static @property JSON emptyArray() { 397 return JSON(cast(JSON[]) null); 398 } 399 400 version (JSONLineNumbers) int line; 401 402 /** 403 Constructor for a JSON object. 404 */ 405 this(typeof(null)) @trusted { 406 m_type = Type.null_; 407 } 408 /// ditto 409 this(bool v) @trusted { 410 m_type = Type.bool_; 411 m_bool = v; 412 } 413 /// ditto 414 this(byte v) { 415 this(cast(long) v); 416 } 417 /// ditto 418 this(ubyte v) { 419 this(cast(long) v); 420 } 421 /// ditto 422 this(short v) { 423 this(cast(long) v); 424 } 425 /// ditto 426 this(ushort v) { 427 this(cast(long) v); 428 } 429 /// ditto 430 this(int v) { 431 this(cast(long) v); 432 } 433 /// ditto 434 this(uint v) { 435 this(cast(long) v); 436 } 437 /// ditto 438 this(long v) @trusted { 439 m_type = Type.int_; 440 m_int = v; 441 } 442 /// ditto 443 this(BigInt v) @trusted { 444 m_type = Type.bigInt; 445 initBigInt(); 446 m_bigInt = v; 447 } 448 /// ditto 449 this(double v) @trusted { 450 m_type = Type.float_; 451 m_float = v; 452 } 453 /// ditto 454 this(string v) @trusted { 455 m_type = Type..string; 456 m_string = v; 457 } 458 /// ditto 459 this(JSON[] v) @trusted { 460 m_type = Type.array; 461 m_array = v; 462 } 463 /// ditto 464 this(JSON[string] v) @trusted { 465 m_type = Type.object; 466 m_object = v; 467 } 468 469 // used internally for UUID serialization support 470 private this(UUID v) { 471 this(v.toString()); 472 } 473 474 /** 475 Converts a std.json.JSONValue object to a vibe JSON object. 476 */ 477 this(in JSONValue value) @safe { 478 final switch (value.type) { 479 case JSONType.null_: 480 this = null; 481 break; 482 case JSONType.object: 483 this = emptyObject; 484 () @trusted { 485 foreach (string k, ref const JSONValue v; value.object) 486 this[k] = JSON(v); 487 }(); 488 break; 489 case JSONType.array: 490 this = (() @trusted => JSON(value.array.map!(a => JSON(a)).array))(); 491 break; 492 case JSONType..string: 493 this = value.str; 494 break; 495 case JSONType.integer: 496 this = value.integer; 497 break; 498 case JSONType.uinteger: 499 this = BigInt(value.uinteger); 500 break; 501 case JSONType.float_: 502 this = value.floating; 503 break; 504 case JSONType.true_: 505 this = true; 506 break; 507 case JSONType.false_: 508 this = false; 509 break; 510 } 511 } 512 513 /** 514 Allows assignment of D values to a JSON value. 515 */ 516 ref JSON opAssign(JSON v) return { 517 if (v.type != Type.bigInt) 518 runDestructors(); 519 auto old_type = m_type; 520 m_type = v.m_type; 521 final switch (m_type) { 522 case Type.undefined: 523 m_string = null; 524 break; 525 case Type.null_: 526 m_string = null; 527 break; 528 case Type.bool_: 529 m_bool = v.m_bool; 530 break; 531 case Type.int_: 532 m_int = v.m_int; 533 break; 534 case Type.bigInt: 535 if (old_type != Type.bigInt) 536 initBigInt(); 537 m_bigInt = v.m_bigInt; 538 break; 539 case Type.float_: 540 m_float = v.m_float; 541 break; 542 case Type..string: 543 m_string = v.m_string; 544 break; 545 case Type.array: 546 opAssign(v.m_array); 547 break; 548 case Type.object: 549 opAssign(v.m_object); 550 break; 551 } 552 return this; 553 } 554 /// ditto 555 void opAssign(typeof(null)) { 556 runDestructors(); 557 m_type = Type.null_; 558 m_string = null; 559 } 560 /// ditto 561 bool opAssign(bool v) { 562 runDestructors(); 563 m_type = Type.bool_; 564 m_bool = v; 565 return v; 566 } 567 /// ditto 568 int opAssign(int v) { 569 runDestructors(); 570 m_type = Type.int_; 571 m_int = v; 572 return v; 573 } 574 /// ditto 575 long opAssign(long v) { 576 runDestructors(); 577 m_type = Type.int_; 578 m_int = v; 579 return v; 580 } 581 /// ditto 582 BigInt opAssign(BigInt v) { 583 if (m_type != Type.bigInt) 584 initBigInt(); 585 m_type = Type.bigInt; 586 m_bigInt = v; 587 return v; 588 } 589 /// ditto 590 double opAssign(double v) { 591 runDestructors(); 592 m_type = Type.float_; 593 m_float = v; 594 return v; 595 } 596 /// ditto 597 string opAssign(string v) { 598 runDestructors(); 599 m_type = Type..string; 600 m_string = v; 601 return v; 602 } 603 /// ditto 604 JSON[] opAssign(JSON[] v) { 605 runDestructors(); 606 m_type = Type.array; 607 m_array = v; 608 version (VibeJSONFieldNames) { 609 foreach (idx, ref av; m_array) 610 av.m_name = format("%s[%s]", m_name, idx); 611 } 612 return v; 613 } 614 /// ditto 615 JSON[string] opAssign(JSON[string] v) { 616 runDestructors(); 617 m_type = Type.object; 618 m_object = v; 619 version (VibeJSONFieldNames) { 620 foreach (key, ref av; m_object) 621 av.m_name = format("%s.%s", m_name, key); 622 } 623 return v; 624 } 625 626 // used internally for UUID serialization support 627 private UUID opAssign(UUID v) { 628 opAssign(v.toString()); 629 return v; 630 } 631 632 /** 633 Allows removal of values from Type.Object JSON objects. 634 */ 635 void remove(string item) { 636 checkType!(JSON[string])(); 637 m_object.remove(item); 638 } 639 640 /** 641 The current type id of this JSON object. 642 */ 643 @property Type type() const @safe { 644 return m_type; 645 } 646 647 /** 648 Clones a JSON value recursively. 649 */ 650 JSON clone() const { 651 final switch (m_type) { 652 case Type.undefined: 653 return JSON.undefined; 654 case Type.null_: 655 return JSON(null); 656 case Type.bool_: 657 return JSON(m_bool); 658 case Type.int_: 659 return JSON(m_int); 660 case Type.bigInt: 661 return JSON(m_bigInt); 662 case Type.float_: 663 return JSON(m_float); 664 case Type..string: 665 return JSON(m_string); 666 case Type.array: 667 JSON[] ret; 668 foreach (v; this.byValue) 669 ret ~= v.clone(); 670 671 return JSON(ret); 672 case Type.object: 673 auto ret = JSON.emptyObject; 674 foreach (name, v; this.byKeyValue) 675 ret[name] = v.clone(); 676 return ret; 677 } 678 } 679 680 /** 681 Allows direct indexing of array typed JSON values. 682 */ 683 ref inout(JSON) opIndex(size_t idx) inout { 684 checkType!(JSON[])(); 685 return m_array[idx]; 686 } 687 688 /// 689 unittest { 690 JSON value = JSON.emptyArray; 691 value ~= 1; 692 value ~= true; 693 value ~= "foo"; 694 assert(value[0] == 1); 695 assert(value[1] == true); 696 assert(value[2] == "foo"); 697 } 698 699 /** 700 Allows direct indexing of object typed JSON values using a string as 701 the key. 702 703 Returns an object of `Type.undefined` if the key was not found. 704 */ 705 const(JSON) opIndex(string key) const { 706 checkType!(JSON[string])(); 707 if (auto pv = key in m_object) 708 return *pv; 709 JSON ret = JSON.undefined; 710 ret.m_string = key; 711 version (VibeJSONFieldNames) 712 ret.m_name = format("%s.%s", m_name, key); 713 return ret; 714 } 715 /// ditto 716 ref JSON opIndex(string key) { 717 checkType!(JSON[string])(); 718 if (auto pv = key in m_object) 719 return *pv; 720 if (m_object is null) { 721 m_object = ["": JSON.init]; 722 m_object.remove(""); 723 } 724 m_object[key] = JSON.init; 725 auto nv = key in m_object; 726 assert(m_object !is null); 727 assert(nv !is null, "Failed to insert key '" ~ key ~ "' into AA!?"); 728 nv.m_type = Type.undefined; 729 assert(nv.type == Type.undefined); 730 nv.m_string = key; 731 version (VibeJSONFieldNames) 732 nv.m_name = format("%s.%s", m_name, key); 733 return *nv; 734 } 735 736 /// 737 unittest { 738 JSON value = JSON.emptyObject; 739 value["a"] = 1; 740 value["b"] = true; 741 value["c"] = "foo"; 742 assert(value["a"] == 1); 743 assert(value["b"] == true); 744 assert(value["c"] == "foo"); 745 assert(value["not-existing"].type() == Type.undefined); 746 } 747 748 /** 749 Returns a slice of a JSON array. 750 */ 751 inout(JSON[]) opSlice() inout { 752 checkType!(JSON[])(); 753 return m_array; 754 } 755 /// 756 inout(JSON[]) opSlice(size_t from, size_t to) inout { 757 checkType!(JSON[])(); 758 return m_array[from .. to]; 759 } 760 761 /** 762 Returns the number of entries of string, array or object typed JSON values. 763 */ 764 @property size_t length() const @trusted { 765 checkType!(string, JSON[], JSON[string])("property length"); 766 switch (m_type) { 767 case Type..string: 768 return m_string.length; 769 case Type.array: 770 return m_array.length; 771 case Type.object: 772 return m_object.length; 773 default: 774 assert(false); 775 } 776 } 777 778 /** 779 Allows foreach iterating over JSON objects and arrays. 780 */ 781 int opApply(scope int delegate(ref JSON obj) del) @system { 782 checkType!(JSON[], JSON[string])("opApply"); 783 if (m_type == Type.array) { 784 foreach (ref v; m_array) 785 if (auto ret = del(v)) 786 return ret; 787 return 0; 788 } else { 789 foreach (ref v; m_object) 790 if (v.type != Type.undefined) 791 if (auto ret = del(v)) 792 return ret; 793 return 0; 794 } 795 } 796 /// ditto 797 int opApply(scope int delegate(ref const JSON obj) del) const @system { 798 checkType!(JSON[], JSON[string])("opApply"); 799 if (m_type == Type.array) { 800 foreach (ref v; m_array) 801 if (auto ret = del(v)) 802 return ret; 803 return 0; 804 } else { 805 foreach (ref v; m_object) 806 if (v.type != Type.undefined) 807 if (auto ret = del(v)) 808 return ret; 809 return 0; 810 } 811 } 812 /// ditto 813 int opApply(scope int delegate(ref size_t idx, ref JSON obj) del) @system { 814 checkType!(JSON[])("opApply"); 815 foreach (idx, ref v; m_array) 816 if (auto ret = del(idx, v)) 817 return ret; 818 return 0; 819 } 820 /// ditto 821 int opApply(scope int delegate(ref size_t idx, ref const JSON obj) del) const @system { 822 checkType!(JSON[])("opApply"); 823 foreach (idx, ref v; m_array) 824 if (auto ret = del(idx, v)) 825 return ret; 826 return 0; 827 } 828 /// ditto 829 int opApply(scope int delegate(ref string idx, ref JSON obj) del) @system { 830 checkType!(JSON[string])("opApply"); 831 foreach (idx, ref v; m_object) 832 if (v.type != Type.undefined) 833 if (auto ret = del(idx, v)) 834 return ret; 835 return 0; 836 } 837 /// ditto 838 int opApply(scope int delegate(ref string idx, ref const JSON obj) del) const @system { 839 checkType!(JSON[string])("opApply"); 840 foreach (idx, ref v; m_object) 841 if (v.type != Type.undefined) 842 if (auto ret = del(idx, v)) 843 return ret; 844 return 0; 845 } 846 847 private alias KeyValue = Tuple!(string, "key", JSON, "value"); 848 849 /// Iterates over all key/value pairs of an object. 850 @property auto byKeyValue() @trusted { 851 checkType!(JSON[string])("byKeyValue"); 852 return m_object.byKeyValue.map!(kv => KeyValue(kv.key, kv.value)).trustedRange; 853 } 854 /// ditto 855 @property auto byKeyValue() const @trusted { 856 checkType!(JSON[string])("byKeyValue"); 857 return m_object.byKeyValue.map!(kv => const(KeyValue)(kv.key, kv.value)).trustedRange; 858 } 859 /// Iterates over all index/value pairs of an array. 860 @property auto byIndexValue() { 861 checkType!(JSON[])("byIndexValue"); 862 return zip(iota(0, m_array.length), m_array); 863 } 864 /// ditto 865 @property auto byIndexValue() const { 866 checkType!(JSON[])("byIndexValue"); 867 return zip(iota(0, m_array.length), m_array); 868 } 869 /// Iterates over all values of an object or array. 870 @property auto byValue() @trusted { 871 checkType!(JSON[], JSON[string])("byValue"); 872 static struct Rng { 873 private { 874 bool isArray; 875 JSON[] array; 876 typeof(JSON.init.m_object.byValue) object; 877 } 878 879 bool empty() @trusted { 880 if (isArray) 881 return array.length == 0; 882 else 883 return object.empty; 884 } 885 886 auto front() @trusted { 887 if (isArray) 888 return array[0]; 889 else 890 return object.front; 891 } 892 893 void popFront() @trusted { 894 if (isArray) 895 array = array[1 .. $]; 896 else 897 object.popFront(); 898 } 899 } 900 901 if (m_type == Type.array) 902 return Rng(true, m_array); 903 else 904 return Rng(false, null, m_object.byValue); 905 } 906 /// ditto 907 @property auto byValue() const @trusted { 908 checkType!(JSON[], JSON[string])("byValue"); 909 static struct Rng { 910 @safe: 911 private { 912 bool isArray; 913 const(JSON)[] array; 914 typeof(const(JSON).init.m_object.byValue) object; 915 } 916 917 bool empty() @trusted { 918 if (isArray) 919 return array.length == 0; 920 else 921 return object.empty; 922 } 923 924 auto front() @trusted { 925 if (isArray) 926 return array[0]; 927 else 928 return object.front; 929 } 930 931 void popFront() @trusted { 932 if (isArray) 933 array = array[1 .. $]; 934 else 935 object.popFront(); 936 } 937 } 938 939 if (m_type == Type.array) 940 return Rng(true, m_array); 941 else 942 return Rng(false, null, m_object.byValue); 943 } 944 945 /** 946 Converts this JSON object to a std.json.JSONValue object 947 */ 948 T opCast(T)() const @safe if (is(T == JSONValue)) { 949 final switch (type) { 950 case JSON.Type.undefined: 951 case JSON.Type.null_: 952 return JSONValue(null); 953 case JSON.Type.bool_: 954 return JSONValue(get!bool); 955 case JSON.Type.int_: 956 return JSONValue(get!long); 957 case JSON.Type.bigInt: 958 auto bi = get!BigInt; 959 if (bi > long.max) 960 return JSONValue((() @trusted => cast(ulong) get!BigInt)()); 961 else 962 return JSONValue((() @trusted => cast(long) get!BigInt)()); 963 case JSON.Type.float_: 964 return JSONValue(get!double); 965 case JSON.Type..string: 966 return JSONValue(get!string); 967 case JSON.Type.array: 968 JSONValue[] ret; 969 foreach (ref const JSON e; byValue) 970 ret ~= cast(JSONValue) e; 971 return JSONValue(ret); 972 case JSON.Type.object: 973 JSONValue[string] ret; 974 foreach (string k, ref const JSON e; byKeyValue) { 975 if (e.type == JSON.Type.undefined) 976 continue; 977 ret[k] = cast(JSONValue) e; 978 } 979 return JSONValue(ret); 980 } 981 } 982 983 /** 984 Converts the JSON value to the corresponding D type - types must match exactly. 985 986 Available_Types: 987 $(UL 988 $(LI `bool` (`Type.bool_`)) 989 $(LI `double` (`Type.float_`)) 990 $(LI `float` (Converted from `double`)) 991 $(LI `long` (`Type.int_`)) 992 $(LI `ulong`, `int`, `uint`, `short`, `ushort`, `byte`, `ubyte` (Converted from `long`)) 993 $(LI `string` (`Type.string`)) 994 $(LI `JSON[]` (`Type.array`)) 995 $(LI `JSON[string]` (`Type.object`)) 996 ) 997 998 See_Also: `opt`, `to`, `deserializeJSON` 999 */ 1000 inout(T) opCast(T)() inout if (!is(T == JSONValue)) { 1001 return get!T; 1002 } 1003 /// ditto 1004 @property inout(T) get(T)() inout @trusted { 1005 static if (!is(T : bool) && is(T : long)) 1006 checkType!(long, BigInt)(); 1007 else 1008 checkType!T(); 1009 1010 static if (is(T == bool)) 1011 return m_bool; 1012 else static if (is(T == double)) 1013 return m_float; 1014 else static if (is(T == float)) 1015 return cast(T) m_float; 1016 else static if (is(T == string)) 1017 return m_string; 1018 else static if (is(T == UUID)) 1019 return UUID(m_string); 1020 else static if (is(T == JSON[])) 1021 return m_array; 1022 else static if (is(T == JSON[string])) 1023 return m_object; 1024 else static if (is(T == BigInt)) 1025 return m_type == Type.bigInt ? m_bigInt : BigInt(m_int); 1026 else static if (is(T : long)) { 1027 if (m_type == Type.bigInt) { 1028 enforceJSON(m_bigInt <= T.max && m_bigInt >= T.min, 1029 "Integer conversion out of bounds error"); 1030 return cast(T) m_bigInt.toLong(); 1031 } else { 1032 enforceJSON(m_int <= T.max && m_int >= T.min, "Integer conversion out of bounds error"); 1033 return cast(T) m_int; 1034 } 1035 } else 1036 static assert(0, 1037 "JSON can only be cast to (bool, long, std.bigint.BigInt, double, string, JSON[] or JSON[string]. Not " 1038 ~ T.stringof ~ "."); 1039 } 1040 1041 /** 1042 Returns the native type for this JSON if it matches the current runtime type. 1043 1044 If the runtime type does not match the given native type, the 'def' parameter is returned 1045 instead. 1046 1047 See_Also: `get` 1048 */ 1049 @property const(T) opt(T)(const(T) def = T.init) const { 1050 if (typeId!T != m_type) 1051 return def; 1052 return get!T; 1053 } 1054 /// ditto 1055 @property T opt(T)(T def = T.init) { 1056 if (typeId!T != m_type) 1057 return def; 1058 return get!T; 1059 } 1060 1061 /** 1062 Converts the JSON value to the corresponding D type - types are converted as necessary. 1063 1064 Automatically performs conversions between strings and numbers. See 1065 `get` for the list of available types. For converting/deserializing 1066 JSON to complex data types see `deserializeJSON`. 1067 1068 See_Also: `get`, `deserializeJSON` 1069 */ 1070 @property inout(T) to(T)() inout { 1071 static if (is(T == bool)) { 1072 final switch (m_type) { 1073 case Type.undefined: 1074 return false; 1075 case Type.null_: 1076 return false; 1077 case Type.bool_: 1078 return m_bool; 1079 case Type.int_: 1080 return m_int != 0; 1081 case Type.bigInt: 1082 return m_bigInt != 0; 1083 case Type.float_: 1084 return m_float != 0; 1085 case Type..string: 1086 return m_string.length > 0; 1087 case Type.array: 1088 return m_array.length > 0; 1089 case Type.object: 1090 return m_object.length > 0; 1091 } 1092 } else static if (is(T == double)) { 1093 final switch (m_type) { 1094 case Type.undefined: 1095 return T.init; 1096 case Type.null_: 1097 return 0; 1098 case Type.bool_: 1099 return m_bool ? 1 : 0; 1100 case Type.int_: 1101 return m_int; 1102 case Type.bigInt: 1103 return bigIntToLong(); 1104 case Type.float_: 1105 return m_float; 1106 case Type..string: 1107 return .to!double(cast(string) m_string); 1108 case Type.array: 1109 return double.init; 1110 case Type.object: 1111 return double.init; 1112 } 1113 } else static if (is(T == float)) { 1114 final switch (m_type) { 1115 case Type.undefined: 1116 return T.init; 1117 case Type.null_: 1118 return 0; 1119 case Type.bool_: 1120 return m_bool ? 1 : 0; 1121 case Type.int_: 1122 return m_int; 1123 case Type.bigInt: 1124 return bigIntToLong(); 1125 case Type.float_: 1126 return m_float; 1127 case Type..string: 1128 return .to!float(cast(string) m_string); 1129 case Type.array: 1130 return float.init; 1131 case Type.object: 1132 return float.init; 1133 } 1134 } else static if (is(T == long)) { 1135 final switch (m_type) { 1136 case Type.undefined: 1137 return 0; 1138 case Type.null_: 1139 return 0; 1140 case Type.bool_: 1141 return m_bool ? 1 : 0; 1142 case Type.int_: 1143 return m_int; 1144 case Type.bigInt: 1145 return cast(long) bigIntToLong(); 1146 case Type.float_: 1147 return cast(long) m_float; 1148 case Type..string: 1149 return .to!long(m_string); 1150 case Type.array: 1151 return 0; 1152 case Type.object: 1153 return 0; 1154 } 1155 } else static if (is(T : long)) { 1156 final switch (m_type) { 1157 case Type.undefined: 1158 return 0; 1159 case Type.null_: 1160 return 0; 1161 case Type.bool_: 1162 return m_bool ? 1 : 0; 1163 case Type.int_: 1164 return cast(T) m_int; 1165 case Type.bigInt: 1166 return cast(T) bigIntToLong(); 1167 case Type.float_: 1168 return cast(T) m_float; 1169 case Type..string: 1170 return cast(T).to!long(cast(string) m_string); 1171 case Type.array: 1172 return 0; 1173 case Type.object: 1174 return 0; 1175 } 1176 } else static if (is(T == string)) { 1177 switch (m_type) { 1178 default: 1179 return toString(); 1180 case Type..string: 1181 return m_string; 1182 } 1183 } else static if (is(T == JSON[])) { 1184 switch (m_type) { 1185 default: 1186 return JSON([this]); 1187 case Type.array: 1188 return m_array; 1189 } 1190 } else static if (is(T == JSON[string])) { 1191 switch (m_type) { 1192 default: 1193 return JSON(["value": this]); 1194 case Type.object: 1195 return m_object; 1196 } 1197 } else static if (is(T == BigInt)) { 1198 final switch (m_type) { 1199 case Type.undefined: 1200 return BigInt(0); 1201 case Type.null_: 1202 return BigInt(0); 1203 case Type.bool_: 1204 return BigInt(m_bool ? 1 : 0); 1205 case Type.int_: 1206 return BigInt(m_int); 1207 case Type.bigInt: 1208 return m_bigInt; 1209 case Type.float_: 1210 return BigInt(cast(long) m_float); 1211 case Type..string: 1212 return BigInt(.to!long(m_string)); 1213 case Type.array: 1214 return BigInt(0); 1215 case Type.object: 1216 return BigInt(0); 1217 } 1218 } else static if (is(T == JSONValue)) { 1219 return cast(JSONValue) this; 1220 } else 1221 static assert(0, 1222 "JSON can only be cast to (bool, long, std.bigint.BigInt, double, string, JSON[] or JSON[string]. Not " 1223 ~ T.stringof ~ "."); 1224 } 1225 1226 /** 1227 Performs unary operations on the JSON value. 1228 1229 The following operations are supported for each type: 1230 1231 $(DL 1232 $(DT Null) $(DD none) 1233 $(DT Bool) $(DD ~) 1234 $(DT Int) $(DD +, -, ++, --) 1235 $(DT Float) $(DD +, -, ++, --) 1236 $(DT String) $(DD none) 1237 $(DT Array) $(DD none) 1238 $(DT Object) $(DD none) 1239 ) 1240 */ 1241 JSON opUnary(string op)() const { 1242 static if (op == "~") { 1243 checkType!bool(); 1244 return JSON(~m_bool); 1245 } else static if (op == "+" || op == "-" || op == "++" || op == "--") { 1246 checkType!(BigInt, long, double)("unary " ~ op); 1247 if (m_type == Type.int_) 1248 mixin("return JSON(" ~ op ~ "m_int);"); 1249 else if (m_type == Type.bigInt) 1250 mixin("return JSON(" ~ op ~ "m_bigInt);"); 1251 else if (m_type == Type.float_) 1252 mixin("return JSON(" ~ op ~ "m_float);"); 1253 else 1254 assert(false); 1255 } else 1256 static assert(0, "Unsupported operator '" ~ op ~ "' for type JSON."); 1257 } 1258 /** 1259 Performs binary operations between JSON values. 1260 1261 The two JSON values must be of the same run time type or a JSONException 1262 will be thrown. Only the operations listed are allowed for each of the 1263 types. 1264 1265 $(DL 1266 $(DT Null) $(DD none) 1267 $(DT Bool) $(DD &&, ||) 1268 $(DT Int) $(DD +, -, *, /, %) 1269 $(DT Float) $(DD +, -, *, /, %) 1270 $(DT String) $(DD ~) 1271 $(DT Array) $(DD ~) 1272 $(DT Object) $(DD in) 1273 ) 1274 */ 1275 JSON opBinary(string op)(ref const(JSON) other) const { 1276 enforceJSON(m_type == other.m_type, 1277 "Binary operation '" ~ op ~ "' between " ~ .to!string( 1278 m_type) ~ " and " ~ .to!string(other.m_type) ~ " JSON objects."); 1279 static if (op == "&&") { 1280 checkType!(bool)(op); 1281 return JSON(m_bool && other.m_bool); 1282 } else static if (op == "||") { 1283 checkType!(bool)(op); 1284 return JSON(m_bool || other.m_bool); 1285 } else static if (op == "+") { 1286 checkType!(BigInt, long, double)(op); 1287 if (m_type == Type.int_) 1288 return JSON(m_int + other.m_int); 1289 else if (m_type == Type.bigInt) 1290 return JSON(() @trusted { return m_bigInt + other.m_bigInt; }()); 1291 else if (m_type == Type.float_) 1292 return JSON(m_float + other.m_float); 1293 else 1294 assert(false); 1295 } else static if (op == "-") { 1296 checkType!(BigInt, long, double)(op); 1297 if (m_type == Type.int_) 1298 return JSON(m_int - other.m_int); 1299 else if (m_type == Type.bigInt) 1300 return JSON(() @trusted { return m_bigInt - other.m_bigInt; }()); 1301 else if (m_type == Type.float_) 1302 return JSON(m_float - other.m_float); 1303 else 1304 assert(false); 1305 } else static if (op == "*") { 1306 checkType!(BigInt, long, double)(op); 1307 if (m_type == Type.int_) 1308 return JSON(m_int * other.m_int); 1309 else if (m_type == Type.bigInt) 1310 return JSON(() @trusted { return m_bigInt * other.m_bigInt; }()); 1311 else if (m_type == Type.float_) 1312 return JSON(m_float * other.m_float); 1313 else 1314 assert(false); 1315 } else static if (op == "/") { 1316 checkType!(BigInt, long, double)(op); 1317 if (m_type == Type.int_) 1318 return JSON(m_int / other.m_int); 1319 else if (m_type == Type.bigInt) 1320 return JSON(() @trusted { return m_bigInt / other.m_bigInt; }()); 1321 else if (m_type == Type.float_) 1322 return JSON(m_float / other.m_float); 1323 else 1324 assert(false); 1325 } else static if (op == "%") { 1326 checkType!(BigInt, long, double)(op); 1327 if (m_type == Type.int_) 1328 return JSON(m_int % other.m_int); 1329 else if (m_type == Type.bigInt) 1330 return JSON(() @trusted { return m_bigInt % other.m_bigInt; }()); 1331 else if (m_type == Type.float_) 1332 return JSON(m_float % other.m_float); 1333 else 1334 assert(false); 1335 } else static if (op == "~") { 1336 checkType!(string, JSON[])(op); 1337 if (m_type == Type..string) 1338 return JSON(m_string ~ other.m_string); 1339 else if (m_type == Type.array) 1340 return JSON(m_array ~ other.m_array); 1341 else 1342 assert(false); 1343 } else 1344 static assert(0, "Unsupported operator '" ~ op ~ "' for type JSON."); 1345 } 1346 /// ditto 1347 JSON opBinary(string op)(JSON other) if (op == "~") { 1348 static if (op == "~") { 1349 checkType!(string, JSON[])(op); 1350 if (m_type == Type..string) 1351 return JSON(m_string ~ other.m_string); 1352 else if (m_type == Type.array) 1353 return JSON(m_array ~ other.m_array); 1354 else 1355 assert(false); 1356 } else 1357 static assert(0, "Unsupported operator '" ~ op ~ "' for type JSON."); 1358 } 1359 /// ditto 1360 void opOpAssign(string op)(JSON other) 1361 if (op == "+" || op == "-" || op == "*" || op == "/" || op == "%" || op == "~") { 1362 enforceJSON(m_type == other.m_type || op == "~" && m_type == Type.array, 1363 "Binary operation '" ~ op ~ "=' between " ~ .to!string( 1364 m_type) ~ " and " ~ .to!string(other.m_type) ~ " JSON objects."); 1365 static if (op == "+") { 1366 if (m_type == Type.int_) 1367 m_int += other.m_int; 1368 else if (m_type == Type.bigInt) 1369 m_bigInt += other.m_bigInt; 1370 else if (m_type == Type.float_) 1371 m_float += other.m_float; 1372 else 1373 enforceJSON(false, "'+=' only allowed for scalar types, not " ~ .to!string(m_type) ~ "."); 1374 } else static if (op == "-") { 1375 if (m_type == Type.int_) 1376 m_int -= other.m_int; 1377 else if (m_type == Type.bigInt) 1378 m_bigInt -= other.m_bigInt; 1379 else if (m_type == Type.float_) 1380 m_float -= other.m_float; 1381 else 1382 enforceJSON(false, "'-=' only allowed for scalar types, not " ~ .to!string(m_type) ~ "."); 1383 } else static if (op == "*") { 1384 if (m_type == Type.int_) 1385 m_int *= other.m_int; 1386 else if (m_type == Type.bigInt) 1387 m_bigInt *= other.m_bigInt; 1388 else if (m_type == Type.float_) 1389 m_float *= other.m_float; 1390 else 1391 enforceJSON(false, "'*=' only allowed for scalar types, not " ~ .to!string(m_type) ~ "."); 1392 } else static if (op == "/") { 1393 if (m_type == Type.int_) 1394 m_int /= other.m_int; 1395 else if (m_type == Type.bigInt) 1396 m_bigInt /= other.m_bigInt; 1397 else if (m_type == Type.float_) 1398 m_float /= other.m_float; 1399 else 1400 enforceJSON(false, "'/=' only allowed for scalar types, not " ~ .to!string(m_type) ~ "."); 1401 } else static if (op == "%") { 1402 if (m_type == Type.int_) 1403 m_int %= other.m_int; 1404 else if (m_type == Type.bigInt) 1405 m_bigInt %= other.m_bigInt; 1406 else if (m_type == Type.float_) 1407 m_float %= other.m_float; 1408 else 1409 enforceJSON(false, "'%=' only allowed for scalar types, not " ~ .to!string(m_type) ~ "."); 1410 } else static if (op == "~") { 1411 if (m_type == Type..string) 1412 m_string ~= other.m_string; 1413 else if (m_type == Type.array) { 1414 if (other.m_type == Type.array) 1415 m_array ~= other.m_array; 1416 else 1417 appendArrayElement(other); 1418 } else 1419 enforceJSON(false, 1420 "'~=' only allowed for string and array types, not " ~ .to!string(m_type) ~ "."); 1421 } else 1422 static assert(0, "Unsupported operator '" ~ op ~ "=' for type JSON."); 1423 } 1424 /// ditto 1425 void opOpAssign(string op, T)(T other) 1426 if (!is(T == JSON) && is(typeof(JSON(other)))) { 1427 opOpAssign!op(JSON(other)); 1428 } 1429 /// ditto 1430 JSON opBinary(string op)(bool other) const { 1431 checkType!bool(); 1432 mixin("return JSON(m_bool " ~ op ~ " other);"); 1433 } 1434 /// ditto 1435 JSON opBinary(string op)(long other) const { 1436 checkType!(long, BigInt)(); 1437 if (m_type == Type.bigInt) 1438 mixin("return JSON(m_bigInt " ~ op ~ " other);"); 1439 else 1440 mixin("return JSON(m_int " ~ op ~ " other);"); 1441 } 1442 /// ditto 1443 JSON opBinary(string op)(BigInt other) const { 1444 checkType!(long, BigInt)(); 1445 if (m_type == Type.bigInt) 1446 mixin("return JSON(m_bigInt " ~ op ~ " other);"); 1447 else 1448 mixin("return JSON(m_int " ~ op ~ " other);"); 1449 } 1450 /// ditto 1451 JSON opBinary(string op)(double other) const { 1452 checkType!double(); 1453 mixin("return JSON(m_float " ~ op ~ " other);"); 1454 } 1455 /// ditto 1456 JSON opBinary(string op)(string other) const { 1457 checkType!string(); 1458 mixin("return JSON(m_string " ~ op ~ " other);"); 1459 } 1460 /// ditto 1461 JSON opBinary(string op)(JSON[] other) { 1462 checkType!(JSON[])(); 1463 mixin("return JSON(m_array " ~ op ~ " other);"); 1464 } 1465 /// ditto 1466 JSON opBinaryRight(string op)(bool other) const { 1467 checkType!bool(); 1468 mixin("return JSON(other " ~ op ~ " m_bool);"); 1469 } 1470 /// ditto 1471 JSON opBinaryRight(string op)(long other) const { 1472 checkType!(long, BigInt)(); 1473 if (m_type == Type.bigInt) 1474 mixin("return JSON(other " ~ op ~ " m_bigInt);"); 1475 else 1476 mixin("return JSON(other " ~ op ~ " m_int);"); 1477 } 1478 /// ditto 1479 JSON opBinaryRight(string op)(BigInt other) const { 1480 checkType!(long, BigInt)(); 1481 if (m_type == Type.bigInt) 1482 mixin("return JSON(other " ~ op ~ " m_bigInt);"); 1483 else 1484 mixin("return JSON(other " ~ op ~ " m_int);"); 1485 } 1486 /// ditto 1487 JSON opBinaryRight(string op)(double other) const { 1488 checkType!double(); 1489 mixin("return JSON(other " ~ op ~ " m_float);"); 1490 } 1491 /// ditto 1492 JSON opBinaryRight(string op)(string other) const if (op == "~") { 1493 checkType!string(); 1494 return JSON(other ~ m_string); 1495 } 1496 /// ditto 1497 JSON opBinaryRight(string op)(JSON[] other) { 1498 checkType!(JSON[])(); 1499 mixin("return JSON(other " ~ op ~ " m_array);"); 1500 } 1501 1502 /** Checks wheter a particular key is set and returns a pointer to it. 1503 1504 For field that don't exist or have a type of `Type.undefined`, 1505 the `in` operator will return `null`. 1506 */ 1507 inout(JSON)* opBinaryRight(string op)(string other) inout if (op == "in") { 1508 checkType!(JSON[string])(); 1509 auto pv = other in m_object; 1510 if (!pv) 1511 return null; 1512 if (pv.type == Type.undefined) 1513 return null; 1514 return pv; 1515 } 1516 1517 /// 1518 unittest { 1519 auto j = JSON.emptyObject; 1520 j["a"] = "foo"; 1521 j["b"] = JSON.undefined; 1522 1523 assert("a" in j); 1524 assert(("a" in j).get!string == "foo"); 1525 assert("b" !in j); 1526 assert("c" !in j); 1527 } 1528 1529 /** 1530 * The append operator will append arrays. This method always appends it's argument as an array element, so nested arrays can be created. 1531 */ 1532 void appendArrayElement(JSON element) { 1533 enforceJSON(m_type == Type.array, 1534 "'appendArrayElement' only allowed for array types, not " ~ .to!string(m_type) ~ "."); 1535 m_array ~= element; 1536 } 1537 1538 /** 1539 Compares two JSON values for equality. 1540 1541 If the two values have different types, they are considered unequal. 1542 This differs with ECMA script, which performs a type conversion before 1543 comparing the values. 1544 */ 1545 1546 bool opEquals(ref const JSON other) const { 1547 if (m_type != other.m_type) 1548 return false; 1549 final switch (m_type) { 1550 case Type.undefined: 1551 return false; 1552 case Type.null_: 1553 return true; 1554 case Type.bool_: 1555 return m_bool == other.m_bool; 1556 case Type.int_: 1557 return m_int == other.m_int; 1558 case Type.bigInt: 1559 return m_bigInt == other.m_bigInt; 1560 case Type.float_: 1561 return m_float == other.m_float; 1562 case Type..string: 1563 return m_string == other.m_string; 1564 case Type.array: 1565 return m_array == other.m_array; 1566 case Type.object: 1567 return m_object == other.m_object; 1568 } 1569 } 1570 /// ditto 1571 bool opEquals(const JSON other) const { 1572 return opEquals(other); 1573 } 1574 /// ditto 1575 bool opEquals(typeof(null)) const { 1576 return m_type == Type.null_; 1577 } 1578 /// ditto 1579 bool opEquals(bool v) const { 1580 return m_type == Type.bool_ && m_bool == v; 1581 } 1582 /// ditto 1583 bool opEquals(int v) const { 1584 return (m_type == Type.int_ && m_int == v) || (m_type == Type.bigInt && m_bigInt == v); 1585 } 1586 /// ditto 1587 bool opEquals(long v) const { 1588 return (m_type == Type.int_ && m_int == v) || (m_type == Type.bigInt && m_bigInt == v); 1589 } 1590 /// ditto 1591 bool opEquals(BigInt v) const { 1592 return (m_type == Type.int_ && m_int == v) || (m_type == Type.bigInt && m_bigInt == v); 1593 } 1594 /// ditto 1595 bool opEquals(double v) const { 1596 return m_type == Type.float_ && m_float == v; 1597 } 1598 /// ditto 1599 bool opEquals(string v) const { 1600 return m_type == Type..string && m_string == v; 1601 } 1602 1603 /** 1604 Compares two JSON values. 1605 1606 If the types of the two values differ, the value with the smaller type 1607 id is considered the smaller value. This differs from ECMA script, which 1608 performs a type conversion before comparing the values. 1609 1610 JSON values of type Object cannot be compared and will throw an 1611 exception. 1612 */ 1613 int opCmp(ref const JSON other) const { 1614 if (m_type != other.m_type) 1615 return m_type < other.m_type ? -1 : 1; 1616 final switch (m_type) { 1617 case Type.undefined: 1618 return 0; 1619 case Type.null_: 1620 return 0; 1621 case Type.bool_: 1622 return m_bool < other.m_bool ? -1 : m_bool == other.m_bool ? 0 : 1; 1623 case Type.int_: 1624 return m_int < other.m_int ? -1 : m_int == other.m_int ? 0 : 1; 1625 case Type.bigInt: 1626 return () @trusted { return m_bigInt < other.m_bigInt; }() ? -1 : m_bigInt == other.m_bigInt 1627 ? 0 : 1; 1628 case Type.float_: 1629 return m_float < other.m_float ? -1 : m_float == other.m_float ? 0 : 1; 1630 case Type..string: 1631 return m_string < other.m_string ? -1 : m_string == other.m_string ? 0 : 1; 1632 case Type.array: 1633 return m_array < other.m_array ? -1 : m_array == other.m_array ? 0 : 1; 1634 case Type.object: 1635 enforceJSON(false, "JSON objects cannot be compared."); 1636 assert(false); 1637 } 1638 } 1639 1640 alias opDollar = length; 1641 1642 /** 1643 Returns the type id corresponding to the given D type. 1644 */ 1645 static @property Type typeId(T)() { 1646 static if (is(T == typeof(null))) 1647 return Type.null_; 1648 else static if (is(T == bool)) 1649 return Type.bool_; 1650 else static if (is(T == double)) 1651 return Type.float_; 1652 else static if (is(T == float)) 1653 return Type.float_; 1654 else static if (is(T : long)) 1655 return Type.int_; 1656 else static if (is(T == string)) 1657 return Type..string; 1658 else static if (is(T == UUID)) 1659 return Type..string; 1660 else static if (is(T == JSON[])) 1661 return Type.array; 1662 else static if (is(T == JSON[string])) 1663 return Type.object; 1664 else static if (is(T == BigInt)) 1665 return Type.bigInt; 1666 else 1667 static assert(false, "Unsupported JSON type '" ~ T.stringof 1668 ~ "'. Only bool, long, std.bigint.BigInt, double, string, JSON[] and JSON[string] are allowed."); 1669 } 1670 1671 /** 1672 Returns the JSON object as a string. 1673 1674 For large JSON values use writeJSONString instead as this function will store the whole string 1675 in memory, whereas writeJSONString writes it out bit for bit. 1676 1677 See_Also: writeJSONString, toPrettyString 1678 */ 1679 string toString() const @trusted { 1680 // DMD BUG: this should actually be all @safe, but for some reason 1681 // @safe inference for writeJSONString doesn't work. 1682 auto ret = appender!string(); 1683 writeJSONString(ret, this); 1684 return ret.data; 1685 } 1686 /// ditto 1687 void toString(scope void delegate(const(char)[]) @safe sink, FormatSpec!char fmt) @trusted { 1688 // DMD BUG: this should actually be all @safe, but for some reason 1689 // @safe inference for writeJSONString doesn't work. 1690 static struct DummyRangeS { 1691 void delegate(const(char)[]) @safe sink; 1692 void put(const(char)[] str) @safe { 1693 sink(str); 1694 } 1695 1696 void put(char ch) @trusted { 1697 sink((&ch)[0 .. 1]); 1698 } 1699 } 1700 1701 auto r = DummyRangeS(sink); 1702 writeJSONString(r, this); 1703 } 1704 /// ditto 1705 void toString(scope void delegate(const(char)[]) @system sink, FormatSpec!char fmt) @system { 1706 // DMD BUG: this should actually be all @safe, but for some reason 1707 // @safe inference for writeJSONString doesn't work. 1708 static struct DummyRange { 1709 void delegate(const(char)[]) sink; 1710 @trusted: 1711 void put(const(char)[] str) { 1712 sink(str); 1713 } 1714 1715 void put(char ch) { 1716 sink((&ch)[0 .. 1]); 1717 } 1718 } 1719 1720 auto r = DummyRange(sink); 1721 writeJSONString(r, this); 1722 } 1723 1724 /** 1725 Returns the JSON object as a "pretty" string. 1726 1727 --- 1728 auto JSON = JSON(["foo": JSON("bar")]); 1729 writeln(JSON.toPrettyString()); 1730 1731 // output: 1732 // { 1733 // "foo": "bar" 1734 // } 1735 --- 1736 1737 Params: 1738 level = Specifies the base amount of indentation for the output. Indentation is always 1739 done using tab characters. 1740 1741 See_Also: writePrettyJSONString, toString 1742 */ 1743 string toPrettyString(int level = 0) const @trusted { 1744 auto ret = appender!string(); 1745 writePrettyJSONString(ret, this, level); 1746 return ret.data; 1747 } 1748 1749 private void checkType(TYPES...)(string op = null) const { 1750 bool matched = false; 1751 foreach (T; TYPES) 1752 if (m_type == typeId!T) 1753 matched = true; 1754 if (matched) 1755 return; 1756 1757 string name; 1758 version (VibeJSONFieldNames) { 1759 if (m_name.length) 1760 name = m_name ~ " of type " ~ m_type.to!string; 1761 else 1762 name = "JSON of type " ~ m_type.to!string; 1763 } else 1764 name = "JSON of type " ~ m_type.to!string; 1765 1766 string expected; 1767 static if (TYPES.length == 1) 1768 expected = typeId!(TYPES[0]).to!string; 1769 else { 1770 foreach (T; TYPES) { 1771 if (expected.length > 0) 1772 expected ~= ", "; 1773 expected ~= typeId!T.to!string; 1774 } 1775 } 1776 1777 if (!op.length) 1778 throw new JSONException(format("Got %s, expected %s.", name, expected)); 1779 else 1780 throw new JSONException(format("Got %s, expected %s for %s.", name, expected, op)); 1781 } 1782 1783 private void initBigInt() @trusted { 1784 BigInt[1] init_; 1785 // BigInt is a struct, and it has a special BigInt.init value, which differs from null. 1786 // m_data has no special initializer and when it tries to first access to BigInt 1787 // via m_bigInt(), we should explicitly initialize m_data with BigInt.init 1788 m_data[0 .. BigInt.sizeof] = cast(void[]) init_; 1789 } 1790 1791 private void runDestructors() { 1792 if (m_type != Type.bigInt) 1793 return; 1794 1795 BigInt init_; 1796 // After swaping, init_ contains the real number from JSON, and it 1797 // will be destroyed when this function is finished. 1798 // m_bigInt now contains static BigInt.init value and destruction may 1799 // be ommited for it. 1800 swap(init_, m_bigInt); 1801 } 1802 1803 private long bigIntToLong() inout { 1804 assert(m_type == Type.bigInt, 1805 format("Converting non-bigInt type with bitIntToLong!?: %s", cast(Type) m_type)); 1806 enforceJSON(m_bigInt >= long.min && m_bigInt <= long.max, 1807 "Number out of range while converting BigInt(" ~ format("%d", m_bigInt) ~ ") to long."); 1808 return m_bigInt.toLong(); 1809 } 1810 1811 /*invariant() 1812 { 1813 assert(m_type >= Type.Undefined && m_type <= Type.Object); 1814 }*/ 1815 } 1816 1817 @safe unittest { // issue #1234 - @safe toString 1818 auto j = JSON(true); 1819 j.toString((str) @safe {}, FormatSpec!char("s")); 1820 assert(j.toString() == "true"); 1821 } 1822 1823 /******************************************************************************/ 1824 /* public functions */ 1825 /******************************************************************************/ 1826 1827 /** 1828 Parses the given range as a JSON string and returns the corresponding JSON object. 1829 1830 The range is shrunk during parsing, leaving any remaining text that is not part of 1831 the JSON contents. 1832 1833 Throws a JSONException if any parsing error occured. 1834 */ 1835 JSON parseJSON(R)(ref R range, int* line = null, string filename = null) 1836 if (is(R == string)) { 1837 JSON ret; 1838 enforceJSON(!range.empty, "JSON string is empty.", filename, 0); 1839 1840 skipWhitespace(range, line); 1841 1842 enforceJSON(!range.empty, "JSON string contains only whitespaces.", filename, 0); 1843 1844 version (JSONLineNumbers) { 1845 int curline = line ? *line : 0; 1846 } 1847 1848 bool minus = false; 1849 switch (range.front) { 1850 case 'f': 1851 enforceJSON(range[1 .. $].startsWith("alse"), 1852 "Expected 'false', got '" ~ range[0 .. min(5, $)] ~ "'.", filename, line); 1853 range.popFrontN(5); 1854 ret = false; 1855 break; 1856 case 'n': 1857 enforceJSON(range[1 .. $].startsWith("ull"), 1858 "Expected 'null', got '" ~ range[0 .. min(4, $)] ~ "'.", filename, line); 1859 range.popFrontN(4); 1860 ret = null; 1861 break; 1862 case 't': 1863 enforceJSON(range[1 .. $].startsWith("rue"), 1864 "Expected 'true', got '" ~ range[0 .. min(4, $)] ~ "'.", filename, line); 1865 range.popFrontN(4); 1866 ret = true; 1867 break; 1868 1869 case '-': 1870 case '0': .. case '9': 1871 bool is_long_overflow; 1872 bool is_float; 1873 auto num = skipNumber(range, is_float, is_long_overflow); 1874 if (is_float) { 1875 ret = to!double(num); 1876 } else if (is_long_overflow) { 1877 ret = () @trusted { return BigInt(num.to!string); }(); 1878 } else { 1879 ret = to!long(num); 1880 } 1881 break; 1882 case '\"': 1883 ret = skipJSONString(range); 1884 break; 1885 case '[': 1886 auto arr = appender!(JSON[]); 1887 range.popFront(); 1888 while (true) { 1889 skipWhitespace(range, line); 1890 enforceJSON(!range.empty, "Missing ']' before EOF.", filename, line); 1891 if (range.front == ']') 1892 break; 1893 arr ~= parseJSON(range, line, filename); 1894 skipWhitespace(range, line); 1895 enforceJSON(!range.empty, "Missing ']' before EOF.", filename, line); 1896 enforceJSON(range.front == ',' || range.front == ']', 1897 format("Expected ']' or ',' - got '%s'.", range.front), filename, line); 1898 if (range.front == ']') 1899 break; 1900 else 1901 range.popFront(); 1902 } 1903 range.popFront(); 1904 ret = arr.data; 1905 break; 1906 case '{': 1907 JSON[string] obj; 1908 range.popFront(); 1909 while (true) { 1910 skipWhitespace(range, line); 1911 enforceJSON(!range.empty, "Missing '}' before EOF.", filename, line); 1912 if (range.front == '}') 1913 break; 1914 string key = skipJSONString(range); 1915 skipWhitespace(range, line); 1916 enforceJSON(range.startsWith(":"), "Expected ':' for key '" ~ key ~ "'", filename, line); 1917 range.popFront(); 1918 skipWhitespace(range, line); 1919 JSON itm = parseJSON(range, line, filename); 1920 obj[key] = itm; 1921 skipWhitespace(range, line); 1922 enforceJSON(!range.empty, "Missing '}' before EOF.", filename, line); 1923 enforceJSON(range.front == ',' || range.front == '}', 1924 format("Expected '}' or ',' - got '%s'.", range.front), filename, line); 1925 if (range.front == '}') 1926 break; 1927 else 1928 range.popFront(); 1929 } 1930 range.popFront(); 1931 ret = obj; 1932 break; 1933 default: 1934 enforceJSON(false, format("Expected valid JSON token, got '%s'.", 1935 range[0 .. min(12, $)]), filename, line); 1936 assert(false); 1937 } 1938 1939 assert(ret.type != JSON.Type.undefined); 1940 version (JSONLineNumbers) 1941 ret.line = curline; 1942 return ret; 1943 } 1944 1945 /** 1946 Parses the given JSON string and returns the corresponding JSON object. 1947 1948 Throws a JSONException if any parsing error occurs. 1949 */ 1950 JSON parseJSONString(string str, string filename = null) @safe { 1951 auto strcopy = str; 1952 int line = 0; 1953 auto ret = parseJSON(strcopy, () @trusted { return &line; }(), filename); 1954 enforceJSON(strcopy.strip().length == 0, 1955 "Expected end of string after JSON value.", filename, line); 1956 return ret; 1957 } 1958 1959 @safe unittest { 1960 // These currently don't work at compile time 1961 assert(parseJSONString("17559991181826658461") == JSON(BigInt(17559991181826658461UL))); 1962 assert(parseJSONString("99999999999999999999999999") == () @trusted { 1963 return JSON(BigInt("99999999999999999999999999")); 1964 }()); 1965 auto json = parseJSONString(`{"hey": "This is @à test éhééhhéhéé !%/??*&?\ud83d\udcec"}`); 1966 assert(json.toPrettyString() == parseJSONString(json.toPrettyString()).toPrettyString()); 1967 1968 bool test() { 1969 assert(parseJSONString("null") == JSON(null)); 1970 assert(parseJSONString("true") == JSON(true)); 1971 assert(parseJSONString("false") == JSON(false)); 1972 assert(parseJSONString("1") == JSON(1)); 1973 assert(parseJSONString("2.0") == JSON(2.0)); 1974 assert(parseJSONString("\"test\"") == JSON("test")); 1975 assert(parseJSONString("[1, 2, 3]") == JSON([JSON(1), JSON(2), JSON(3)])); 1976 assert(parseJSONString("{\"a\": 1}") == JSON(["a": JSON(1)])); 1977 assert(parseJSONString(`"\\\/\b\f\n\r\t\u1234"`).get!string == "\\/\b\f\n\r\t\u1234"); 1978 1979 return true; 1980 } 1981 1982 // Run at compile time and runtime 1983 assert(test()); 1984 static assert(test()); 1985 } 1986 1987 @safe unittest { 1988 bool test() { 1989 try 1990 parseJSONString(" \t\n "); 1991 catch (Exception e) 1992 assert(e.msg.endsWith("JSON string contains only whitespaces.")); 1993 try 1994 parseJSONString(`{"a": 1`); 1995 catch (Exception e) 1996 assert(e.msg.endsWith("Missing '}' before EOF.")); 1997 try 1998 parseJSONString(`{"a": 1 x`); 1999 catch (Exception e) 2000 assert(e.msg.endsWith("Expected '}' or ',' - got 'x'.")); 2001 try 2002 parseJSONString(`[1`); 2003 catch (Exception e) 2004 assert(e.msg.endsWith("Missing ']' before EOF.")); 2005 try 2006 parseJSONString(`[1 x`); 2007 catch (Exception e) 2008 assert(e.msg.endsWith("Expected ']' or ',' - got 'x'.")); 2009 2010 return true; 2011 } 2012 2013 // Run at compile time and runtime 2014 assert(test()); 2015 static assert(test()); 2016 } 2017 2018 /** 2019 Serializes the given value to JSON. 2020 2021 The following types of values are supported: 2022 2023 $(DL 2024 $(DT `JSON`) $(DD Used as-is) 2025 $(DT `null`) $(DD Converted to `JSON.Type.null_`) 2026 $(DT `bool`) $(DD Converted to `JSON.Type.bool_`) 2027 $(DT `float`, `double`) $(DD Converted to `JSON.Type.float_`) 2028 $(DT `short`, `ushort`, `int`, `uint`, `long`, `ulong`) $(DD Converted to `JSON.Type.int_`) 2029 $(DT `BigInt`) $(DD Converted to `JSON.Type.bigInt`) 2030 $(DT `string`) $(DD Converted to `JSON.Type.string`) 2031 $(DT `T[]`) $(DD Converted to `JSON.Type.array`) 2032 $(DT `T[string]`) $(DD Converted to `JSON.Type.object`) 2033 $(DT `struct`) $(DD Converted to `JSON.Type.object`) 2034 $(DT `class`) $(DD Converted to `JSON.Type.object` or `JSON.Type.null_`) 2035 ) 2036 2037 All entries of an array or an associative array, as well as all R/W properties and 2038 all public fields of a struct/class are recursively serialized using the same rules. 2039 2040 Fields ending with an underscore will have the last underscore stripped in the 2041 serialized output. This makes it possible to use fields with D keywords as their name 2042 by simply appending an underscore. 2043 2044 The following methods can be used to customize the serialization of structs/classes: 2045 2046 --- 2047 JSON toJSON() const; 2048 static T fromJSON(JSON src); 2049 2050 string toString() const; 2051 static T fromString(string src); 2052 --- 2053 2054 The methods will have to be defined in pairs. The first pair that is implemented by 2055 the type will be used for serialization (i.e. `toJSON` overrides `toString`). 2056 2057 See_Also: `deserializeJSON`, `vibe.data.serialization` 2058 */ 2059 JSON serializeToJSON(T)(auto ref T value) { 2060 return serialize!JSONSerializer(value); 2061 } 2062 /// ditto 2063 void serializeToJSON(R, T)(R destination, auto ref T value) 2064 if (isOutputRange!(R, char) || isOutputRange!(R, ubyte)) { 2065 serialize!(JSONStringSerializer!R)(value, destination); 2066 } 2067 /// ditto 2068 string serializeToJSONString(T)(auto ref T value) { 2069 auto ret = appender!string; 2070 serializeToJSON(ret, value); 2071 return ret.data; 2072 } 2073 2074 /// 2075 @safe unittest { 2076 struct Foo { 2077 int number; 2078 string str; 2079 } 2080 2081 Foo f; 2082 2083 f.number = 12; 2084 f.str = "hello"; 2085 2086 string json = serializeToJSONString(f); 2087 assert(json == `{"number":12,"str":"hello"}`); 2088 JSON value = serializeToJSON(f); 2089 assert(value.type == JSON.Type.object); 2090 assert(value["number"] == JSON(12)); 2091 assert(value["str"] == JSON("hello")); 2092 } 2093 2094 /** 2095 Serializes the given value to a pretty printed JSON string. 2096 2097 See_also: `serializeToJSON`, `vibe.data.serialization` 2098 */ 2099 void serializeToPrettyJSON(R, T)(R destination, auto ref T value) 2100 if (isOutputRange!(R, char) || isOutputRange!(R, ubyte)) { 2101 serialize!(JSONStringSerializer!(R, true))(value, destination); 2102 } 2103 /// ditto 2104 string serializeToPrettyJSON(T)(auto ref T value) { 2105 auto ret = appender!string; 2106 serializeToPrettyJSON(ret, value); 2107 return ret.data; 2108 } 2109 2110 /// 2111 @safe unittest { 2112 struct Foo { 2113 int number; 2114 string str; 2115 } 2116 2117 Foo f; 2118 f.number = 12; 2119 f.str = "hello"; 2120 2121 string json = serializeToPrettyJSON(f); 2122 assert(json == `{ 2123 "number": 12, 2124 "str": "hello" 2125 }`); 2126 } 2127 2128 /** 2129 Deserializes a JSON value into the destination variable. 2130 2131 The same types as for `serializeToJSON()` are supported and handled inversely. 2132 2133 See_Also: `serializeToJSON`, `serializeToJSONString`, `vibe.data.serialization` 2134 */ 2135 void deserializeJSON(T)(ref T dst, JSON src) { 2136 dst = deserializeJSON!T(src); 2137 } 2138 /// ditto 2139 T deserializeJSON(T)(JSON src) { 2140 return deserialize!(JSONSerializer, T)(src); 2141 } 2142 /// ditto 2143 T deserializeJSON(T, R)(R input) if (!is(R == JSON) && isInputRange!R) { 2144 return deserialize!(JSONStringSerializer!R, T)(input); 2145 } 2146 2147 /// 2148 @safe unittest { 2149 struct Foo { 2150 int number; 2151 string str; 2152 } 2153 2154 Foo f = deserializeJSON!Foo(`{"number": 12, "str": "hello"}`); 2155 assert(f.number == 12); 2156 assert(f.str == "hello"); 2157 } 2158 2159 @safe unittest { 2160 import std.stdio; 2161 2162 enum Foo : string { 2163 k = "test" 2164 } 2165 2166 enum Boo : int { 2167 l = 5 2168 } 2169 2170 static struct S { 2171 float a; 2172 double b; 2173 bool c; 2174 int d; 2175 string e; 2176 byte f; 2177 ubyte g; 2178 long h; 2179 ulong i; 2180 float[] j; 2181 Foo k; 2182 Boo l; 2183 } 2184 2185 immutable S t = { 2186 1.5, -3.0, true, int.min, "Test", -128, 255, long.min, ulong.max, [ 2187 1.1, 1.2, 1.3 2188 ], Foo.k, Boo.l}; 2189 S u; 2190 deserializeJSON(u, serializeToJSON(t)); 2191 assert(t.a == u.a); 2192 assert(t.b == u.b); 2193 assert(t.c == u.c); 2194 assert(t.d == u.d); 2195 assert(t.e == u.e); 2196 assert(t.f == u.f); 2197 assert(t.g == u.g); 2198 assert(t.h == u.h); 2199 assert(t.i == u.i); 2200 assert(t.j == u.j); 2201 assert(t.k == u.k); 2202 assert(t.l == u.l); 2203 } 2204 2205 @safe unittest { 2206 assert(uint.max == serializeToJSON(uint.max).deserializeJSON!uint); 2207 assert(ulong.max == serializeToJSON(ulong.max).deserializeJSON!ulong); 2208 } 2209 2210 unittest { 2211 static struct A { 2212 int value; 2213 static A fromJSON(JSON val) @safe { 2214 return A(val.get!int); 2215 } 2216 2217 JSON toJSON() const @safe { 2218 return JSON(value); 2219 } 2220 } 2221 2222 static struct C { 2223 int value; 2224 static C fromString(string val) @safe { 2225 return C(val.to!int); 2226 } 2227 2228 string toString() const @safe { 2229 return value.to!string; 2230 } 2231 } 2232 2233 static struct D { 2234 int value; 2235 } 2236 2237 assert(serializeToJSON(const A(123)) == JSON(123)); 2238 assert(serializeToJSON(A(123)) == JSON(123)); 2239 assert(serializeToJSON(const C(123)) == JSON("123")); 2240 assert(serializeToJSON(C(123)) == JSON("123")); 2241 assert(serializeToJSON(const D(123)) == serializeToJSON(["value": 123])); 2242 assert(serializeToJSON(D(123)) == serializeToJSON(["value": 123])); 2243 } 2244 2245 unittest { 2246 auto d = Date(2001, 1, 1); 2247 deserializeJSON(d, serializeToJSON(Date.init)); 2248 assert(d == Date.init); 2249 deserializeJSON(d, serializeToJSON(Date(2001, 1, 1))); 2250 assert(d == Date(2001, 1, 1)); 2251 struct S { 2252 immutable(int)[] x; 2253 } 2254 2255 S s; 2256 deserializeJSON(s, serializeToJSON(S([1, 2, 3]))); 2257 assert(s == S([1, 2, 3])); 2258 struct T { 2259 @optional S s; 2260 @optional int i; 2261 @optional float f_; // underscore strip feature 2262 @optional double d; 2263 @optional string str; 2264 } 2265 2266 auto t = T(S([1, 2, 3])); 2267 deserializeJSON(t, 2268 parseJSONString(`{ "s" : null, "i" : null, "f" : null, "d" : null, "str" : null }`)); 2269 assert(text(t) == text(T())); 2270 } 2271 2272 unittest { 2273 static class C { 2274 @safe: 2275 int a; 2276 private int _b; 2277 @property int b() const { 2278 return _b; 2279 } 2280 2281 @property void b(int v) { 2282 _b = v; 2283 } 2284 2285 @property int test() const { 2286 return 10; 2287 } 2288 2289 void test2() { 2290 } 2291 } 2292 2293 C c = new C; 2294 c.a = 1; 2295 c.b = 2; 2296 2297 C d; 2298 deserializeJSON(d, serializeToJSON(c)); 2299 assert(c.a == d.a); 2300 assert(c.b == d.b); 2301 } 2302 2303 unittest { 2304 static struct C { 2305 @safe: 2306 int value; 2307 static C fromString(string val) { 2308 return C(val.to!int); 2309 } 2310 2311 string toString() const { 2312 return value.to!string; 2313 } 2314 } 2315 2316 enum Color { 2317 Red, 2318 Green, 2319 Blue 2320 } 2321 2322 { 2323 static class T { 2324 @safe: 2325 string[Color] enumIndexedMap; 2326 string[C] stringableIndexedMap; 2327 this() { 2328 enumIndexedMap = [Color.Red: "magenta", Color.Blue: "deep blue"]; 2329 stringableIndexedMap = [C(42): "forty-two"]; 2330 } 2331 } 2332 2333 T original = new T; 2334 original.enumIndexedMap[Color.Green] = "olive"; 2335 T other; 2336 deserializeJSON(other, serializeToJSON(original)); 2337 assert(serializeToJSON(other) == serializeToJSON(original)); 2338 } 2339 { 2340 static struct S { 2341 string[Color] enumIndexedMap; 2342 string[C] stringableIndexedMap; 2343 } 2344 2345 S* original = new S; 2346 original.enumIndexedMap = [Color.Red: "magenta", Color.Blue: "deep blue"]; 2347 original.enumIndexedMap[Color.Green] = "olive"; 2348 original.stringableIndexedMap = [C(42): "forty-two"]; 2349 S other; 2350 deserializeJSON(other, serializeToJSON(original)); 2351 assert(serializeToJSON(other) == serializeToJSON(original)); 2352 } 2353 } 2354 2355 unittest { 2356 import std.typecons : Nullable; 2357 2358 struct S { 2359 Nullable!int a, b; 2360 } 2361 2362 S s; 2363 s.a = 2; 2364 2365 auto j = serializeToJSON(s); 2366 assert(j["a"].type == JSON.Type.int_); 2367 assert(j["b"].type == JSON.Type.null_); 2368 2369 auto t = deserializeJSON!S(j); 2370 assert(!t.a.isNull() && t.a == 2); 2371 assert(t.b.isNull()); 2372 } 2373 2374 unittest { // #840 2375 int[2][2] nestedArray = 1; 2376 assert(nestedArray.serializeToJSON.deserializeJSON!(typeof(nestedArray)) == nestedArray); 2377 } 2378 2379 unittest { // #1109 2380 static class C { 2381 @safe: 2382 int mem; 2383 this(int m) { 2384 mem = m; 2385 } 2386 2387 static C fromJSON(JSON j) { 2388 return new C(j.get!int - 1); 2389 } 2390 2391 JSON toJSON() const { 2392 return JSON(mem + 1); 2393 } 2394 } 2395 2396 const c = new C(13); 2397 assert(serializeToJSON(c) == JSON(14)); 2398 assert(deserializeJSON!C(JSON(14)).mem == 13); 2399 } 2400 2401 unittest { // const and mutable JSON 2402 JSON j = JSON(1); 2403 const k = JSON(2); 2404 assert(serializeToJSON(j) == JSON(1)); 2405 assert(serializeToJSON(k) == JSON(2)); 2406 } 2407 2408 unittest { // issue #1660 - deserialize AA whose key type is string-based enum 2409 enum Foo : string { 2410 Bar = "bar", 2411 Buzz = "buzz" 2412 } 2413 2414 struct S { 2415 int[Foo] f; 2416 } 2417 2418 const s = S([Foo.Bar: 2000]); 2419 assert(serializeToJSON(s)["f"] == JSON([Foo.Bar: JSON(2000)])); 2420 2421 auto j = JSON.emptyObject; 2422 j["f"] = [Foo.Bar: JSON(2000)]; 2423 assert(deserializeJSON!S(j).f == [Foo.Bar: 2000]); 2424 } 2425 2426 unittest { 2427 struct V { 2428 UUID v; 2429 } 2430 2431 const u = UUID("318d7a61-e41b-494e-90d3-0a99f5531bfe"); 2432 const s = `{"v":"318d7a61-e41b-494e-90d3-0a99f5531bfe"}`; 2433 auto j = JSON(["v": JSON(u)]); 2434 2435 const v = V(u); 2436 2437 assert(serializeToJSON(v) == j); 2438 2439 j = JSON.emptyObject; 2440 j["v"] = u; 2441 assert(deserializeJSON!V(j).v == u); 2442 2443 assert(serializeToJSONString(v) == s); 2444 assert(deserializeJSON!V(s).v == u); 2445 } 2446 2447 /** 2448 Serializer for a plain JSON representation. 2449 2450 See_Also: vibe.data.serialization.serialize, vibe.data.serialization.deserialize, serializeToJSON, deserializeJSON 2451 */ 2452 struct JSONSerializer { 2453 template isJSONBasicType(T) { 2454 enum isJSONBasicType = std.traits.isNumeric!T || isBoolean!T 2455 || isSomeString!T || is(T == typeof(null)) || is(Unqual!T == UUID) || isJSONSerializable!T; 2456 } 2457 2458 template isSupportedValueType(T) { 2459 enum isSupportedValueType = isJSONBasicType!T || is(Unqual!T == JSON) 2460 || is(Unqual!T == JSONValue); 2461 } 2462 2463 private { 2464 JSON m_current; 2465 JSON[] m_compositeStack; 2466 } 2467 2468 this(JSON data) @safe { 2469 m_current = data; 2470 } 2471 2472 @disable this(this); 2473 2474 // 2475 // serialization 2476 // 2477 JSON getSerializedResult() @safe { 2478 return m_current; 2479 } 2480 2481 void beginWriteDictionary(Traits)() { 2482 m_compositeStack ~= JSON.emptyObject; 2483 } 2484 2485 void endWriteDictionary(Traits)() { 2486 m_current = m_compositeStack[$ - 1]; 2487 m_compositeStack.length--; 2488 } 2489 2490 void beginWriteDictionaryEntry(Traits)(string name) { 2491 } 2492 2493 void endWriteDictionaryEntry(Traits)(string name) { 2494 m_compositeStack[$ - 1][name] = m_current; 2495 } 2496 2497 void beginWriteArray(Traits)(size_t) { 2498 m_compositeStack ~= JSON.emptyArray; 2499 } 2500 2501 void endWriteArray(Traits)() { 2502 m_current = m_compositeStack[$ - 1]; 2503 m_compositeStack.length--; 2504 } 2505 2506 void beginWriteArrayEntry(Traits)(size_t) { 2507 } 2508 2509 void endWriteArrayEntry(Traits)(size_t) { 2510 m_compositeStack[$ - 1].appendArrayElement(m_current); 2511 } 2512 2513 void writeValue(Traits, T)(auto ref T value) if (!is(Unqual!T == JSON)) { 2514 alias UT = Unqual!T; 2515 static if (is(UT == JSONValue)) { 2516 m_current = JSON(value); 2517 } else static if (isJSONSerializable!UT) { 2518 static if (!__traits(compiles, ()@safe { return value.toJSON(); }())) 2519 pragma(msg, 2520 "Non-@safe toJSON/fromJSON methods are deprecated - annotate " 2521 ~ UT.stringof ~ ".toJSON() with @safe."); 2522 m_current = () @trusted { return value.toJSON(); }(); 2523 } else static if (isSomeString!T && !is(UT == string)) { 2524 writeValue!Traits(value.to!string); 2525 } else 2526 m_current = JSON(value); 2527 } 2528 2529 void writeValue(Traits, T)(auto ref T value) if (is(T == JSON)) { 2530 m_current = value; 2531 } 2532 2533 void writeValue(Traits, T)(auto ref T value) 2534 if (!is(T == JSON) && is(T : const(JSON))) { 2535 m_current = value.clone; 2536 } 2537 2538 // 2539 // deserialization 2540 // 2541 void readDictionary(Traits)(scope void delegate(string) @safe field_handler) { 2542 enforceJSON(m_current.type == JSON.Type.object, 2543 "Expected JSON object, got " ~ m_current.type.to!string); 2544 auto old = m_current; 2545 foreach (string key, value; m_current.get!(JSON[string])) { 2546 if (value.type == JSON.Type.undefined) { 2547 continue; 2548 } 2549 2550 m_current = value; 2551 field_handler(key); 2552 } 2553 m_current = old; 2554 } 2555 2556 void beginReadDictionaryEntry(Traits)(string name) { 2557 } 2558 2559 void endReadDictionaryEntry(Traits)(string name) { 2560 } 2561 2562 void readArray(Traits)(scope void delegate(size_t) @safe size_callback, 2563 scope void delegate() @safe entry_callback) { 2564 enforceJSON(m_current.type == JSON.Type.array, 2565 "Expected JSON array, got " ~ m_current.type.to!string); 2566 auto old = m_current; 2567 size_callback(m_current.length); 2568 foreach (ent; old.get!(JSON[])) { 2569 m_current = ent; 2570 entry_callback(); 2571 } 2572 m_current = old; 2573 } 2574 2575 void beginReadArrayEntry(Traits)(size_t index) { 2576 } 2577 2578 void endReadArrayEntry(Traits)(size_t index) { 2579 } 2580 2581 T readValue(Traits, T)() @safe { 2582 static if (is(T == JSON)) 2583 return m_current; 2584 else static if (is(T == JSONValue)) 2585 return cast(JSONValue) m_current; 2586 else static if (isJSONSerializable!T) { 2587 static if (!__traits(compiles, ()@safe { return T.fromJSON(m_current); }())) 2588 pragma(msg, 2589 "Non-@safe toJSON/fromJSON methods are deprecated - annotate " 2590 ~ T.stringof ~ ".fromJSON() with @safe."); 2591 return () @trusted { return T.fromJSON(m_current); }(); 2592 } else static if (is(T == float) || is(T == double)) { 2593 switch (m_current.type) { 2594 default: 2595 return cast(T) m_current.get!long; 2596 case JSON.Type.null_: 2597 goto case; 2598 case JSON.Type.undefined: 2599 return T.nan; 2600 case JSON.Type.float_: 2601 return cast(T) m_current.get!double; 2602 case JSON.Type.bigInt: 2603 return cast(T) m_current.bigIntToLong(); 2604 } 2605 } else static if (is(T == const(char)[])) { 2606 return readValue!(Traits, string); 2607 } else static if (isSomeString!T && !is(T == string)) { 2608 return readValue!(Traits, string).to!T; 2609 } else static if (is(T == string)) { 2610 if (m_current.type == JSON.Type.array) { // legacy support for pre-#2150 serialization results 2611 return () @trusted { // appender 2612 auto r = appender!string; 2613 foreach (s; m_current.get!(JSON[])) 2614 r.put(s.get!string()); 2615 return r.data; 2616 }(); 2617 } else 2618 return m_current.get!T(); 2619 } else 2620 return m_current.get!T(); 2621 } 2622 2623 bool tryReadNull(Traits)() { 2624 return m_current.type == JSON.Type.null_; 2625 } 2626 } 2627 2628 unittest { 2629 struct T { 2630 @optional string a; 2631 } 2632 2633 auto obj = JSON.emptyObject; 2634 obj["a"] = JSON.undefined; 2635 assert(obj.deserializeJSON!T.a == ""); 2636 } 2637 2638 unittest { 2639 class C { 2640 this(JSON j) { 2641 foo = j; 2642 } 2643 2644 JSON foo; 2645 } 2646 2647 const C c = new C(JSON(42)); 2648 assert(serializeToJSON(c)["foo"].get!int == 42); 2649 } 2650 2651 /** 2652 Serializer for a range based plain JSON string representation. 2653 2654 See_Also: vibe.data.serialization.serialize, vibe.data.serialization.deserialize, serializeToJSON, deserializeJSON 2655 */ 2656 struct JSONStringSerializer(R, bool pretty = false) 2657 if (isInputRange!R || isOutputRange!(R, char)) { 2658 private { 2659 R m_range; 2660 size_t m_level = 0; 2661 } 2662 2663 template isJSONBasicType(T) { 2664 enum isJSONBasicType = std.traits.isNumeric!T || isBoolean!T 2665 || isSomeString!T || is(T == typeof(null)) || is(Unqual!T == UUID) || isJSONSerializable!T; 2666 } 2667 2668 template isSupportedValueType(T) { 2669 enum isSupportedValueType = isJSONBasicType!(Unqual!T) 2670 || is(Unqual!T == JSON) || is(Unqual!T == JSONValue); 2671 } 2672 2673 this(R range) { 2674 m_range = range; 2675 } 2676 2677 @disable this(this); 2678 2679 // 2680 // serialization 2681 // 2682 static if (isOutputRange!(R, char)) { 2683 private { 2684 bool m_firstInComposite; 2685 } 2686 2687 void getSerializedResult() { 2688 } 2689 2690 void beginWriteDictionary(Traits)() { 2691 startComposite(); 2692 m_range.put('{'); 2693 } 2694 2695 void endWriteDictionary(Traits)() { 2696 endComposite(); 2697 m_range.put("}"); 2698 } 2699 2700 void beginWriteDictionaryEntry(Traits)(string name) { 2701 startCompositeEntry(); 2702 m_range.put('"'); 2703 m_range.JSONEscape(name); 2704 static if (pretty) 2705 m_range.put(`": `); 2706 else 2707 m_range.put(`":`); 2708 } 2709 2710 void endWriteDictionaryEntry(Traits)(string name) { 2711 } 2712 2713 void beginWriteArray(Traits)(size_t) { 2714 startComposite(); 2715 m_range.put('['); 2716 } 2717 2718 void endWriteArray(Traits)() { 2719 endComposite(); 2720 m_range.put(']'); 2721 } 2722 2723 void beginWriteArrayEntry(Traits)(size_t) { 2724 startCompositeEntry(); 2725 } 2726 2727 void endWriteArrayEntry(Traits)(size_t) { 2728 } 2729 2730 void writeValue(Traits, T)(in T value) { 2731 alias UT = Unqual!T; 2732 static if (is(T == typeof(null))) 2733 m_range.put("null"); 2734 else static if (is(UT == bool)) 2735 m_range.put(value ? "true" : "false"); 2736 else static if (is(UT : long)) 2737 m_range.formattedWrite("%s", value); 2738 else static if (is(UT == BigInt)) 2739 () @trusted { m_range.formattedWrite("%d", value); }(); 2740 else static if (is(UT : real)) 2741 value == value ? m_range.formattedWrite("%.16g", value) : m_range.put("null"); 2742 else static if (is(UT : const(char)[])) { 2743 m_range.put('"'); 2744 m_range.JSONEscape(value); 2745 m_range.put('"'); 2746 } else static if (isSomeString!T) 2747 writeValue!Traits(value.to!string); // TODO: avoid memory allocation 2748 else static if (is(UT == UUID)) 2749 writeValue!Traits(value.toString()); 2750 else static if (is(UT == JSON)) 2751 m_range.writeJSONString(value); 2752 else static if (is(UT == JSONValue)) 2753 m_range.writeJSONString(JSON(value)); 2754 else static if (isJSONSerializable!UT) { 2755 static if (!__traits(compiles, ()@safe { return value.toJSON(); }())) 2756 pragma(msg, 2757 "Non-@safe toJSON/fromJSON methods are deprecated - annotate " 2758 ~ UT.stringof ~ ".toJSON() with @safe."); 2759 m_range.writeJSONString!(R, pretty)(() @trusted { 2760 return value.toJSON(); 2761 }(), m_level); 2762 } else 2763 static assert(false, "Unsupported type: " ~ UT.stringof); 2764 } 2765 2766 private void startComposite() { 2767 static if (pretty) 2768 m_level++; 2769 m_firstInComposite = true; 2770 } 2771 2772 private void startCompositeEntry() { 2773 if (!m_firstInComposite) { 2774 m_range.put(','); 2775 } else { 2776 m_firstInComposite = false; 2777 } 2778 static if (pretty) 2779 indent(); 2780 } 2781 2782 private void endComposite() { 2783 static if (pretty) { 2784 m_level--; 2785 if (!m_firstInComposite) 2786 indent(); 2787 } 2788 m_firstInComposite = false; 2789 } 2790 2791 private void indent() { 2792 m_range.put('\n'); 2793 foreach (i; 0 .. m_level) 2794 m_range.put('\t'); 2795 } 2796 } 2797 2798 // 2799 // deserialization 2800 // 2801 static if (isInputRange!(R)) { 2802 private { 2803 int m_line = 0; 2804 } 2805 2806 void readDictionary(Traits)(scope void delegate(string) @safe entry_callback) { 2807 m_range.skipWhitespace(&m_line); 2808 enforceJSON(!m_range.empty && m_range.front == '{', "Expecting object."); 2809 m_range.popFront(); 2810 bool first = true; 2811 while (true) { 2812 m_range.skipWhitespace(&m_line); 2813 enforceJSON(!m_range.empty, "Missing '}'."); 2814 if (m_range.front == '}') { 2815 m_range.popFront(); 2816 break; 2817 } else if (!first) { 2818 enforceJSON(m_range.front == ',', 2819 "Expecting ',' or '}', not '" ~ m_range.front.to!string ~ "'."); 2820 m_range.popFront(); 2821 m_range.skipWhitespace(&m_line); 2822 } else 2823 first = false; 2824 2825 auto name = m_range.skipJSONString(&m_line); 2826 2827 m_range.skipWhitespace(&m_line); 2828 enforceJSON(!m_range.empty && m_range.front == ':', 2829 "Expecting ':', not '" ~ m_range.front.to!string ~ "'."); 2830 m_range.popFront(); 2831 2832 entry_callback(name); 2833 } 2834 } 2835 2836 void beginReadDictionaryEntry(Traits)(string name) { 2837 } 2838 2839 void endReadDictionaryEntry(Traits)(string name) { 2840 } 2841 2842 void readArray(Traits)(scope void delegate(size_t) @safe size_callback, 2843 scope void delegate() @safe entry_callback) { 2844 m_range.skipWhitespace(&m_line); 2845 enforceJSON(!m_range.empty && m_range.front == '[', "Expecting array."); 2846 m_range.popFront(); 2847 bool first = true; 2848 while (true) { 2849 m_range.skipWhitespace(&m_line); 2850 enforceJSON(!m_range.empty, "Missing ']'."); 2851 if (m_range.front == ']') { 2852 m_range.popFront(); 2853 break; 2854 } else if (!first) { 2855 enforceJSON(m_range.front == ',', "Expecting ',' or ']'."); 2856 m_range.popFront(); 2857 } else 2858 first = false; 2859 2860 entry_callback(); 2861 } 2862 } 2863 2864 void beginReadArrayEntry(Traits)(size_t index) { 2865 } 2866 2867 void endReadArrayEntry(Traits)(size_t index) { 2868 } 2869 2870 T readValue(Traits, T)() { 2871 m_range.skipWhitespace(&m_line); 2872 static if (is(T == typeof(null))) { 2873 enforceJSON(m_range.take(4).equal("null"), "Expecting 'null'."); 2874 return null; 2875 } else static if (is(T == bool)) { 2876 bool ret = m_range.front == 't'; 2877 string expected = ret ? "true" : "false"; 2878 foreach (ch; expected) { 2879 enforceJSON(m_range.front == ch, "Expecting 'true' or 'false'."); 2880 m_range.popFront(); 2881 } 2882 return ret; 2883 } else static if (is(T : long)) { 2884 bool is_float; 2885 bool is_long_overflow; 2886 auto num = m_range.skipNumber(is_float, is_long_overflow); 2887 enforceJSON(!is_float, "Expecting integer number."); 2888 enforceJSON(!is_long_overflow, num.to!string ~ " is too big for long."); 2889 return to!T(num); 2890 } else static if (is(T : BigInt)) { 2891 bool is_float; 2892 bool is_long_overflow; 2893 auto num = m_range.skipNumber(is_float, is_long_overflow); 2894 enforceJSON(!is_float, "Expecting integer number."); 2895 return BigInt(num); 2896 } else static if (is(T : real)) { 2897 bool is_float; 2898 bool is_long_overflow; 2899 auto num = m_range.skipNumber(is_float, is_long_overflow); 2900 return to!T(num); 2901 } else static if (is(T == string) || is(T == const(char)[])) { 2902 if (!m_range.empty && m_range.front == '[') { 2903 return () @trusted { // appender 2904 auto ret = appender!string(); 2905 readArray!Traits((sz) {}, () @trusted { 2906 ret.put(m_range.skipJSONString(&m_line)); 2907 }); 2908 return ret.data; 2909 }(); 2910 } else 2911 return m_range.skipJSONString(&m_line); 2912 } else static if (isSomeString!T) 2913 return readValue!(Traits, string).to!T; 2914 else static if (is(T == UUID)) 2915 return UUID(readValue!(Traits, string)()); 2916 else static if (is(T == JSON)) 2917 return m_range.parseJSON(&m_line); 2918 else static if (is(T == JSONValue)) 2919 return cast(JSONValue) m_range.parseJSON(&m_line); 2920 else static if (isJSONSerializable!T) { 2921 static if (!__traits(compiles, ()@safe { return T.fromJSON(JSON.init); }())) 2922 pragma(msg, 2923 "Non-@safe toJSON/fromJSON methods are deprecated - annotate " 2924 ~ T.stringof ~ ".fromJSON() with @safe."); 2925 return () @trusted { return T.fromJSON(m_range.parseJSON(&m_line)); }(); 2926 } else 2927 static assert(false, "Unsupported type: " ~ T.stringof); 2928 } 2929 2930 bool tryReadNull(Traits)() { 2931 m_range.skipWhitespace(&m_line); 2932 if (m_range.front != 'n') 2933 return false; 2934 foreach (ch; "null") { 2935 enforceJSON(m_range.front == ch, "Expecting 'null'."); 2936 m_range.popFront(); 2937 } 2938 assert(m_range.empty || m_range.front != 'l'); 2939 return true; 2940 } 2941 } 2942 } 2943 2944 /// Cloning JSON arrays 2945 unittest { 2946 JSON value = JSON([JSON([JSON.emptyArray]), JSON.emptyArray]).clone; 2947 2948 assert(value.length == 2); 2949 assert(value[0].length == 1); 2950 assert(value[0][0].length == 0); 2951 } 2952 2953 unittest { 2954 assert(serializeToJSONString(double.nan) == "null"); 2955 assert(serializeToJSONString(JSON()) == "null"); 2956 assert(serializeToJSONString(JSON(["bar": JSON("baz"), "foo": JSON()])) == `{"bar":"baz"}`); 2957 2958 struct Foo { 2959 JSON bar = JSON(); 2960 } 2961 2962 Foo f; 2963 assert(serializeToJSONString(f) == `{"bar":null}`); 2964 } 2965 2966 /** 2967 Writes the given JSON object as a JSON string into the destination range. 2968 2969 This function will convert the given JSON value to a string without adding 2970 any white space between tokens (no newlines, no indentation and no padding). 2971 The output size is thus minimized, at the cost of bad human readability. 2972 2973 Params: 2974 dst = References the string output range to which the result is written. 2975 JSON = Specifies the JSON value that is to be stringified. 2976 level = Specifies the base amount of indentation for the output. Indentation is always 2977 done using tab characters. 2978 2979 See_Also: JSON.toString, writePrettyJSONString 2980 */ 2981 void writeJSONString(R, bool pretty = false)(ref R dst, in JSON json, size_t level = 0) @safe // if( isOutputRange!R && is(ElementEncodingType!R == char) ) 2982 { 2983 final switch (json.type) { 2984 case JSON.Type.undefined: 2985 dst.put("null"); 2986 break; 2987 case JSON.Type.null_: 2988 dst.put("null"); 2989 break; 2990 case JSON.Type.bool_: 2991 dst.put(json.get!bool ? "true" : "false"); 2992 break; 2993 case JSON.Type.int_: 2994 formattedWrite(dst, "%d", json.get!long); 2995 break; 2996 case JSON.Type.bigInt: 2997 () @trusted { formattedWrite(dst, "%d", json.get!BigInt); }(); 2998 break; 2999 case JSON.Type.float_: 3000 auto d = json.get!double; 3001 if (d != d) 3002 dst.put("null"); // JSON has no NaN value so set null 3003 else 3004 formattedWrite(dst, "%.16g", json.get!double); 3005 break; 3006 case JSON.Type..string: 3007 dst.put('\"'); 3008 JSONEscape(dst, json.get!string); 3009 dst.put('\"'); 3010 break; 3011 case JSON.Type.array: 3012 dst.put('['); 3013 bool first = true; 3014 foreach (ref const JSON e; json.byValue) { 3015 if (!first) 3016 dst.put(","); 3017 first = false; 3018 static if (pretty) { 3019 dst.put('\n'); 3020 foreach (tab; 0 .. level + 1) 3021 dst.put('\t'); 3022 } 3023 if (e.type == JSON.Type.undefined) 3024 dst.put("null"); 3025 else 3026 writeJSONString!(R, pretty)(dst, e, level + 1); 3027 } 3028 static if (pretty) { 3029 if (json.length > 0) { 3030 dst.put('\n'); 3031 foreach (tab; 0 .. level) 3032 dst.put('\t'); 3033 } 3034 } 3035 dst.put(']'); 3036 break; 3037 case JSON.Type.object: 3038 dst.put('{'); 3039 bool first = true; 3040 foreach (string k, ref const JSON e; json.byKeyValue) { 3041 if (e.type == JSON.Type.undefined) 3042 continue; 3043 if (!first) 3044 dst.put(','); 3045 first = false; 3046 static if (pretty) { 3047 dst.put('\n'); 3048 foreach (tab; 0 .. level + 1) 3049 dst.put('\t'); 3050 } 3051 dst.put('\"'); 3052 JSONEscape(dst, k); 3053 dst.put(pretty ? `": ` : `":`); 3054 writeJSONString!(R, pretty)(dst, e, level + 1); 3055 } 3056 static if (pretty) { 3057 if (json.length > 0) { 3058 dst.put('\n'); 3059 foreach (tab; 0 .. level) 3060 dst.put('\t'); 3061 } 3062 } 3063 dst.put('}'); 3064 break; 3065 } 3066 } 3067 3068 unittest { 3069 auto a = JSON.emptyObject; 3070 a["a"] = JSON.emptyArray; 3071 a["b"] = JSON.emptyArray; 3072 a["b"] ~= JSON(1); 3073 a["b"] ~= JSON.emptyObject; 3074 3075 assert(a.toString() == `{"a":[],"b":[1,{}]}` || a.toString() == `{"b":[1,{}],"a":[]}`); 3076 assert(a.toPrettyString() == `{ 3077 "a": [], 3078 "b": [ 3079 1, 3080 {} 3081 ] 3082 }` || a.toPrettyString() == `{ 3083 "b": [ 3084 1, 3085 {} 3086 ], 3087 "a": [] 3088 }`); 3089 } 3090 3091 unittest { // #735 3092 auto a = JSON.emptyArray; 3093 a ~= "a"; 3094 a ~= JSON(); 3095 a ~= "b"; 3096 a ~= null; 3097 a ~= "c"; 3098 assert(a.toString() == `["a",null,"b",null,"c"]`); 3099 } 3100 3101 unittest { 3102 auto a = JSON.emptyArray; 3103 a ~= JSON(1); 3104 a ~= JSON(2); 3105 a ~= JSON(3); 3106 a ~= JSON(4); 3107 a ~= JSON(5); 3108 3109 auto b = JSON(a[0 .. a.length]); 3110 assert(a == b); 3111 3112 auto c = JSON(a[0 .. $]); 3113 assert(a == c); 3114 assert(b == c); 3115 3116 auto d = [JSON(1), JSON(2), JSON(3)]; 3117 assert(d == a[0 .. a.length - 2]); 3118 assert(d == a[0 .. $ - 2]); 3119 } 3120 3121 unittest { 3122 auto j = JSON(double.init); 3123 3124 assert(j.toString == "null"); // A double nan should serialize to null 3125 j = 17.04f; 3126 assert(j.toString == "17.04"); // A proper double should serialize correctly 3127 3128 double d; 3129 deserializeJSON(d, JSON.undefined); // JSON.undefined should deserialize to nan 3130 assert(d != d); 3131 deserializeJSON(d, JSON(null)); // JSON.undefined should deserialize to nan 3132 assert(d != d); 3133 } 3134 /** 3135 Writes the given JSON object as a prettified JSON string into the destination range. 3136 3137 The output will contain newlines and indents to make the output human readable. 3138 3139 Params: 3140 dst = References the string output range to which the result is written. 3141 JSON = Specifies the JSON value that is to be stringified. 3142 level = Specifies the base amount of indentation for the output. Indentation is always 3143 done using tab characters. 3144 3145 See_Also: JSON.toPrettyString, writeJSONString 3146 */ 3147 void writePrettyJSONString(R)(ref R dst, in JSON json, int level = 0) // if( isOutputRange!R && is(ElementEncodingType!R == char) ) 3148 { 3149 writeJSONString!(R, true)(dst, json, level); 3150 } 3151 3152 /** 3153 Helper function that escapes all Unicode characters in a JSON string. 3154 */ 3155 string convertJSONToASCII(string json) { 3156 auto ret = appender!string; 3157 JSONEscape!true(ret, json); 3158 return ret.data; 3159 } 3160 3161 /// private 3162 private void JSONEscape(bool escape_unicode = false, R)(ref R dst, const(char)[] s) { 3163 size_t startPos = 0; 3164 3165 void putInterval(size_t curPos) { 3166 if (curPos > startPos) 3167 dst.put(s[startPos .. curPos]); 3168 startPos = curPos + 1; 3169 } 3170 3171 for (size_t pos = 0; pos < s.length; pos++) { 3172 immutable(char) ch = s[pos]; 3173 3174 switch (ch) { 3175 default: 3176 static if (escape_unicode) { 3177 if (ch <= 0x20 || ch >= 0x80) { 3178 putInterval(pos); 3179 import std.utf : decode; 3180 3181 int len; 3182 dchar codepoint = decode(s, pos); 3183 /* codepoint is in BMP */ 3184 if (codepoint < 0x10000) { 3185 dst.formattedWrite("\\u%04X", codepoint); 3186 } /* not in BMP -> construct a UTF-16 surrogate pair */ 3187 else { 3188 int first, last; 3189 3190 codepoint -= 0x10000; 3191 first = 0xD800 | ((codepoint & 0xffc00) >> 10); 3192 last = 0xDC00 | (codepoint & 0x003ff); 3193 3194 dst.formattedWrite("\\u%04X\\u%04X", first, last); 3195 } 3196 startPos = pos; 3197 pos -= 1; 3198 } 3199 } else { 3200 if (ch < 0x20) { 3201 putInterval(pos); 3202 dst.formattedWrite("\\u%04X", ch); 3203 } 3204 } 3205 break; 3206 case '\\': 3207 putInterval(pos); 3208 dst.put("\\\\"); 3209 break; 3210 case '\r': 3211 putInterval(pos); 3212 dst.put("\\r"); 3213 break; 3214 case '\n': 3215 putInterval(pos); 3216 dst.put("\\n"); 3217 break; 3218 case '\t': 3219 putInterval(pos); 3220 dst.put("\\t"); 3221 break; 3222 case '\"': 3223 putInterval(pos); 3224 dst.put("\\\""); 3225 break; 3226 case '/': 3227 // this avoids the sequence "</" in the output, which is prone 3228 // to cross site scripting attacks when inserted into web pages 3229 if (pos > 0 && s[pos - 1] == '<') { 3230 putInterval(pos); 3231 dst.put("\\/"); 3232 } 3233 break; 3234 } 3235 } 3236 // last interval 3237 putInterval(s.length); 3238 } 3239 3240 /// private 3241 private string JSONUnescape(R)(ref R range) { 3242 auto ret = appender!string(); 3243 while (!range.empty) { 3244 auto ch = range.front; 3245 switch (ch) { 3246 case '"': 3247 return ret.data; 3248 case '\\': 3249 range.popFront(); 3250 enforceJSON(!range.empty, "Unterminated string escape sequence."); 3251 switch (range.front) { 3252 default: 3253 enforceJSON(false, "Invalid string escape sequence."); 3254 break; 3255 case '"': 3256 ret.put('\"'); 3257 range.popFront(); 3258 break; 3259 case '\\': 3260 ret.put('\\'); 3261 range.popFront(); 3262 break; 3263 case '/': 3264 ret.put('/'); 3265 range.popFront(); 3266 break; 3267 case 'b': 3268 ret.put('\b'); 3269 range.popFront(); 3270 break; 3271 case 'f': 3272 ret.put('\f'); 3273 range.popFront(); 3274 break; 3275 case 'n': 3276 ret.put('\n'); 3277 range.popFront(); 3278 break; 3279 case 'r': 3280 ret.put('\r'); 3281 range.popFront(); 3282 break; 3283 case 't': 3284 ret.put('\t'); 3285 range.popFront(); 3286 break; 3287 case 'u': 3288 3289 dchar decode_unicode_escape() { 3290 enforceJSON(range.front == 'u'); 3291 range.popFront(); 3292 dchar uch = 0; 3293 foreach (i; 0 .. 4) { 3294 uch *= 16; 3295 enforceJSON(!range.empty, "Unicode sequence must be '\\uXXXX'."); 3296 auto dc = range.front; 3297 range.popFront(); 3298 3299 if (dc >= '0' && dc <= '9') 3300 uch += dc - '0'; 3301 else if (dc >= 'a' && dc <= 'f') 3302 uch += dc - 'a' + 10; 3303 else if (dc >= 'A' && dc <= 'F') 3304 uch += dc - 'A' + 10; 3305 else 3306 enforceJSON(false, "Unicode sequence must be '\\uXXXX'."); 3307 } 3308 return uch; 3309 } 3310 3311 auto uch = decode_unicode_escape(); 3312 3313 if (0xD800 <= uch && uch <= 0xDBFF) { 3314 /* surrogate pair */ 3315 range.popFront(); // backslash '\' 3316 auto uch2 = decode_unicode_escape(); 3317 enforceJSON(0xDC00 <= uch2 && uch2 <= 0xDFFF, "invalid Unicode"); 3318 { 3319 /* valid second surrogate */ 3320 uch = ((uch - 0xD800) << 10) + (uch2 - 0xDC00) + 0x10000; 3321 } 3322 } 3323 ret.put(uch); 3324 break; 3325 } 3326 break; 3327 default: 3328 ret.put(ch); 3329 range.popFront(); 3330 break; 3331 } 3332 } 3333 return ret.data; 3334 } 3335 3336 private auto skipNumber(R)(ref R s, out bool is_float, out bool is_long_overflow) @safe 3337 if (isNarrowString!R) { 3338 auto r = s.representation; 3339 version (assert) auto rEnd = (() @trusted => r.ptr + r.length - 1)(); 3340 auto res = skipNumber(r, is_float, is_long_overflow); 3341 version (assert) 3342 assert(rEnd == (() @trusted => r.ptr + r.length - 1)()); // check nothing taken off the end 3343 s = s[$ - r.length .. $]; 3344 return res.assumeUTF(); 3345 } 3346 3347 /// private 3348 private auto skipNumber(R)(ref R s, out bool is_float, out bool is_long_overflow) 3349 if (!isNarrowString!R && isForwardRange!R) { 3350 auto sOrig = s.save; 3351 size_t idx = 0; 3352 is_float = false; 3353 is_long_overflow = false; 3354 ulong int_part = 0; 3355 if (s.front == '-') { 3356 s.popFront(); 3357 ++idx; 3358 } 3359 if (s.front == '0') { 3360 s.popFront(); 3361 ++idx; 3362 } else { 3363 enforceJSON(isDigit(s.front), "Digit expected at beginning of number."); 3364 int_part = s.front - '0'; 3365 s.popFront(); 3366 ++idx; 3367 while (!s.empty && isDigit(s.front)) { 3368 if (!is_long_overflow) { 3369 auto dig = s.front - '0'; 3370 if ((long.max / 10) > int_part || ((long.max / 10) == int_part && (long.max % 10) >= dig)) { 3371 int_part *= 10; 3372 int_part += dig; 3373 } else { 3374 is_long_overflow = true; 3375 } 3376 } 3377 s.popFront(); 3378 ++idx; 3379 } 3380 } 3381 3382 if (!s.empty && s.front == '.') { 3383 s.popFront(); 3384 ++idx; 3385 is_float = true; 3386 while (!s.empty && isDigit(s.front)) { 3387 s.popFront(); 3388 ++idx; 3389 } 3390 } 3391 3392 if (!s.empty && (s.front == 'e' || s.front == 'E')) { 3393 s.popFront(); 3394 ++idx; 3395 is_float = true; 3396 if (!s.empty && (s.front == '+' || s.front == '-')) { 3397 s.popFront(); 3398 ++idx; 3399 } 3400 enforceJSON(!s.empty && isDigit(s.front), 3401 "Expected exponent." ~ sOrig.takeExactly(idx).to!string); 3402 s.popFront(); 3403 ++idx; 3404 while (!s.empty && isDigit(s.front)) { 3405 s.popFront(); 3406 ++idx; 3407 } 3408 } 3409 3410 return sOrig.takeExactly(idx); 3411 } 3412 3413 unittest { 3414 import std.meta : AliasSeq; 3415 3416 // test for string and for a simple range 3417 foreach (foo; AliasSeq!(to!string, map!"a")) { 3418 auto test_1 = foo("9223372036854775806"); // lower then long.max 3419 auto test_2 = foo("9223372036854775807"); // long.max 3420 auto test_3 = foo("9223372036854775808"); // greater then long.max 3421 bool is_float; 3422 bool is_long_overflow; 3423 test_1.skipNumber(is_float, is_long_overflow); 3424 assert(!is_long_overflow); 3425 test_2.skipNumber(is_float, is_long_overflow); 3426 assert(!is_long_overflow); 3427 test_3.skipNumber(is_float, is_long_overflow); 3428 assert(is_long_overflow); 3429 } 3430 } 3431 3432 /// private 3433 private string skipJSONString(R)(ref R s, int* line = null) { 3434 // TODO: count or disallow any newlines inside of the string 3435 enforceJSON(!s.empty && s.front == '"', "Expected '\"' to start string."); 3436 s.popFront(); 3437 string ret = JSONUnescape(s); 3438 enforceJSON(!s.empty && s.front == '"', "Expected '\"' to terminate string."); 3439 s.popFront(); 3440 return ret; 3441 } 3442 3443 /// private 3444 private void skipWhitespace(R)(ref R s, int* line = null) { 3445 while (!s.empty) { 3446 switch (s.front) { 3447 default: 3448 return; 3449 case ' ', '\t': 3450 s.popFront(); 3451 break; 3452 case '\n': 3453 s.popFront(); 3454 if (!s.empty && s.front == '\r') 3455 s.popFront(); 3456 if (line) 3457 (*line)++; 3458 break; 3459 case '\r': 3460 s.popFront(); 3461 if (!s.empty && s.front == '\n') 3462 s.popFront(); 3463 if (line) 3464 (*line)++; 3465 break; 3466 } 3467 } 3468 } 3469 3470 private bool isDigit(dchar ch) @safe nothrow pure { 3471 return ch >= '0' && ch <= '9'; 3472 } 3473 3474 private string underscoreStrip(string field_name) @safe nothrow pure { 3475 if (field_name.length < 1 || field_name[$ - 1] != '_') 3476 return field_name; 3477 else 3478 return field_name[0 .. $ - 1]; 3479 } 3480 3481 /// private 3482 package template isJSONSerializable(T) { 3483 enum isJSONSerializable = is(typeof(T.init.toJSON()) : JSON) 3484 && is(typeof(T.fromJSON(JSON())) : T); 3485 } 3486 3487 private void enforceJSON(string file = __FILE__, size_t line = __LINE__)( 3488 bool cond, lazy string message = "JSON exception") { 3489 import dutils.data.utils.exception : enforce; 3490 3491 enforce!JSONException(cond, message, file, line); 3492 } 3493 3494 private void enforceJSON(string file = __FILE__, size_t line = __LINE__)( 3495 bool cond, lazy string message, string err_file, int err_line) { 3496 import dutils.data.utils.exception : enforce; 3497 3498 enforce!JSONException(cond, format("%s(%s): Error: %s", err_file, 3499 err_line + 1, message), file, line); 3500 } 3501 3502 private void enforceJSON(string file = __FILE__, size_t line = __LINE__)( 3503 bool cond, lazy string message, string err_file, int* err_line) { 3504 enforceJSON!(file, line)(cond, message, err_file, err_line ? *err_line : -1); 3505 } 3506 3507 private auto trustedRange(R)(R range) { 3508 static struct Rng { 3509 private R range; 3510 @property bool empty() @trusted { 3511 return range.empty; 3512 } 3513 3514 @property auto front() @trusted { 3515 return range.front; 3516 } 3517 3518 void popFront() @trusted { 3519 range.popFront(); 3520 } 3521 } 3522 3523 return Rng(range); 3524 } 3525 3526 // make sure JSON is usable for CTFE 3527 @safe unittest { 3528 static assert(is(typeof({ 3529 struct Test { 3530 JSON object_ = JSON.emptyObject; 3531 JSON array = JSON.emptyArray; 3532 } 3533 })), "CTFE for JSON type failed."); 3534 3535 static JSON test() { 3536 JSON j; 3537 j = JSON(42); 3538 j = JSON([JSON(true)]); 3539 j = JSON(["foo": JSON(null)]); 3540 j = JSON("foo"); 3541 return j; 3542 } 3543 3544 enum j = test(); 3545 static assert(j == JSON("foo")); 3546 } 3547 3548 @safe unittest { // XSS prevention 3549 assert(JSON("</script>some/path").toString() == `"<\/script>some/path"`); 3550 assert(serializeToJSONString("</script>some/path") == `"<\/script>some/path"`); 3551 } 3552 3553 @system unittest { // Recursive structures 3554 static struct Bar { 3555 Bar[] foos; 3556 int i; 3557 } 3558 3559 auto b = deserializeJSON!Bar(`{"i":1,"foos":[{"foos":[],"i":2}]}`); 3560 assert(b.i == 1); 3561 assert(b.foos.length == 1); 3562 assert(b.foos[0].i == 2); 3563 assert(b.foos[0].foos.length == 0); 3564 } 3565 3566 @safe unittest { // JSON <-> std.json.JSONValue 3567 auto astr = `{ 3568 "null": null, 3569 "string": "Hello", 3570 "integer": 123456, 3571 "uinteger": 18446744073709551614, 3572 "float": 12.34, 3573 "object": { "hello": "world" }, 3574 "array": [1, 2, "string"], 3575 "true": true, 3576 "false": false 3577 }`; 3578 auto a = parseJSONString(astr); 3579 3580 // test JSONValue -> JSON conversion 3581 assert(JSON(cast(JSONValue) a) == a); 3582 3583 // test JSON -> JSONValue conversion 3584 auto v = cast(JSONValue) a; 3585 assert(deserializeJSON!JSONValue(serializeToJSON(v)) == v); 3586 3587 // test JSON strint <-> JSONValue serialization 3588 assert(deserializeJSON!JSONValue(astr) == v); 3589 assert(parseJSONString(serializeToJSONString(v)) == a); 3590 3591 // test using std.conv for the conversion 3592 import std.conv : to; 3593 3594 assert(a.to!JSONValue 3595 .to!JSON == a); 3596 assert(to!JSON(to!JSONValue(a)) == a); 3597 } 3598 3599 @safe unittest { // issue #2150 - serialization of const/mutable strings + wide character strings 3600 assert(serializeToJSON(cast(const(char)[]) "foo") == JSON("foo")); 3601 assert(serializeToJSON("foo".dup) == JSON("foo")); 3602 assert(deserializeJSON!string(JSON("foo")) == "foo"); 3603 assert(deserializeJSON!string(JSON([JSON("f"), JSON("o"), JSON("o")])) == "foo"); 3604 assert(serializeToJSONString(cast(const(char)[]) "foo") == "\"foo\""); 3605 assert(deserializeJSON!string("\"foo\"") == "foo"); 3606 3607 assert(serializeToJSON(cast(const(wchar)[]) "foo"w) == JSON("foo")); 3608 assert(serializeToJSON("foo"w.dup) == JSON("foo")); 3609 assert(deserializeJSON!wstring(JSON("foo")) == "foo"); 3610 assert(deserializeJSON!wstring(JSON([JSON("f"), JSON("o"), JSON("o")])) == "foo"); 3611 assert(serializeToJSONString(cast(const(wchar)[]) "foo"w) == "\"foo\""); 3612 assert(deserializeJSON!wstring("\"foo\"") == "foo"); 3613 3614 assert(serializeToJSON(cast(const(dchar)[]) "foo"d) == JSON("foo")); 3615 assert(serializeToJSON("foo"d.dup) == JSON("foo")); 3616 assert(deserializeJSON!dstring(JSON("foo")) == "foo"); 3617 assert(deserializeJSON!dstring(JSON([JSON("f"), JSON("o"), JSON("o")])) == "foo"); 3618 assert(serializeToJSONString(cast(const(dchar)[]) "foo"d) == "\"foo\""); 3619 assert(deserializeJSON!dstring("\"foo\"") == "foo"); 3620 }