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