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