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