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_data) 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.range; 29 import std..string : format; 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) return 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 the $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 /** 697 Compares two JSON values for equality. 698 699 If the two values have different types, they are considered unequal. 700 This differs with ECMA script, which performs a type conversion before 701 comparing the values. 702 */ 703 bool opEquals(ref const Json other) 704 const { 705 if( m_type != other.m_type ) return false; 706 final switch(m_type){ 707 case Type.undefined: return false; 708 case Type.null_: return true; 709 case Type.bool_: return m_bool == other.m_bool; 710 case Type.int_: return m_int == other.m_int; 711 case Type.float_: return m_float == other.m_float; 712 case Type..string: return m_string == other.m_string; 713 case Type.array: return m_array == other.m_array; 714 case Type.object: return m_object == other.m_object; 715 } 716 } 717 /// ditto 718 bool opEquals(const Json other) const { return opEquals(other); } 719 /// ditto 720 bool opEquals(typeof(null)) const { return m_type == Type.null_; } 721 /// ditto 722 bool opEquals(bool v) const { return m_type == Type.bool_ && m_bool == v; } 723 /// ditto 724 bool opEquals(int v) const { return m_type == Type.int_ && m_int == v; } 725 /// ditto 726 bool opEquals(long v) const { return m_type == Type.int_ && m_int == v; } 727 /// ditto 728 bool opEquals(double v) const { return m_type == Type.float_ && m_float == v; } 729 /// ditto 730 bool opEquals(string v) const { return m_type == Type..string && m_string == v; } 731 732 /** 733 Compares two JSON values. 734 735 If the types of the two values differ, the value with the smaller type 736 id is considered the smaller value. This differs from ECMA script, which 737 performs a type conversion before comparing the values. 738 739 JSON values of type Object cannot be compared and will throw an 740 exception. 741 */ 742 int opCmp(ref const Json other) 743 const { 744 if( m_type != other.m_type ) return m_type < other.m_type ? -1 : 1; 745 final switch(m_type){ 746 case Type.undefined: return 0; 747 case Type.null_: return 0; 748 case Type.bool_: return m_bool < other.m_bool ? -1 : m_bool == other.m_bool ? 0 : 1; 749 case Type.int_: return m_int < other.m_int ? -1 : m_int == other.m_int ? 0 : 1; 750 case Type.float_: return m_float < other.m_float ? -1 : m_float == other.m_float ? 0 : 1; 751 case Type..string: return m_string < other.m_string ? -1 : m_string == other.m_string ? 0 : 1; 752 case Type.array: return m_array < other.m_array ? -1 : m_array == other.m_array ? 0 : 1; 753 case Type.object: 754 enforceJson(false, "JSON objects cannot be compared."); 755 assert(false); 756 } 757 } 758 759 alias opDollar = length; 760 761 /** 762 Returns the type id corresponding to the given D type. 763 */ 764 static @property Type typeId(T)() { 765 static if( is(T == typeof(null)) ) return Type.null_; 766 else static if( is(T == bool) ) return Type.bool_; 767 else static if( is(T == double) ) return Type.float_; 768 else static if( is(T == float) ) return Type.float_; 769 else static if( is(T : long) ) return Type.int_; 770 else static if( is(T == string) ) return Type..string; 771 else static if( is(T == Json[]) ) return Type.array; 772 else static if( is(T == Json[string]) ) return Type.object; 773 else static assert(false, "Unsupported JSON type '"~T.stringof~"'. Only bool, long, double, string, Json[] and Json[string] are allowed."); 774 } 775 776 /** 777 Returns the JSON object as a string. 778 779 For large JSON values use writeJsonString instead as this function will store the whole string 780 in memory, whereas writeJsonString writes it out bit for bit. 781 782 See_Also: writeJsonString, toPrettyString 783 */ 784 string toString() 785 const { 786 auto ret = appender!string(); 787 writeJsonString(ret, this); 788 return ret.data; 789 } 790 791 /** 792 Returns the JSON object as a "pretty" string. 793 794 --- 795 auto json = Json(["foo": Json("bar")]); 796 writeln(json.toPrettyString()); 797 798 // output: 799 // { 800 // "foo": "bar" 801 // } 802 --- 803 804 Params: 805 level = Specifies the base amount of indentation for the output. Indentation is always 806 done using tab characters. 807 808 See_Also: writePrettyJsonString, toString 809 */ 810 string toPrettyString(int level = 0) 811 const { 812 auto ret = appender!string(); 813 writePrettyJsonString(ret, this, level); 814 return ret.data; 815 } 816 817 private void checkType(TYPES...)(string op = null) 818 const { 819 bool matched = false; 820 foreach (T; TYPES) if (m_type == typeId!T) matched = true; 821 if (matched) return; 822 823 string name; 824 version (VibeJsonFieldNames) { 825 if (m_name.length) name = m_name ~ " of type " ~ m_type.to!string; 826 else name = "JSON of type " ~ m_type.to!string; 827 } else name = "JSON of type " ~ m_type.to!string; 828 829 string expected; 830 static if (TYPES.length == 1) expected = typeId!(TYPES[0]).to!string; 831 else { 832 foreach (T; TYPES) { 833 if (expected.length > 0) expected ~= ", "; 834 expected ~= typeId!T.to!string; 835 } 836 } 837 838 enforceJson(op.length > 0, format("Got %s, expected %s.", name, expected), m_fileName, line); 839 enforceJson(false, format("Got %s, expected %s for %s.", name, expected, op), m_fileName, line); 840 } 841 842 /*invariant() 843 { 844 assert(m_type >= Type.Undefined && m_type <= Type.Object); 845 }*/ 846 } 847 848 849 /******************************************************************************/ 850 /* public functions */ 851 /******************************************************************************/ 852 853 /** 854 Parses the given range as a JSON string and returns the corresponding Json object. 855 856 The range is shrunk during parsing, leaving any remaining text that is not part of 857 the JSON contents. 858 859 Throws a JSONException if any parsing error occurred. 860 */ 861 Json parseJson(R)(ref R range, int* line = null, string filename = null) 862 if( is(R == string) ) 863 { 864 import std..string : startsWith; 865 866 Json ret; 867 enforceJson(!range.empty, "JSON string is empty.", filename, 0); 868 869 skipWhitespace(range, line); 870 871 version(JsonLineNumbers) { 872 import dub.internal.vibecompat.core.log; 873 int curline = line ? *line : 0; 874 } 875 876 switch( range.front ){ 877 case 'f': 878 enforceJson(range[1 .. $].startsWith("alse"), "Expected 'false', got '"~range[0 .. min(5, $)]~"'.", filename, line); 879 range.popFrontN(5); 880 ret = false; 881 break; 882 case 'n': 883 enforceJson(range[1 .. $].startsWith("ull"), "Expected 'null', got '"~range[0 .. min(4, $)]~"'.", filename, line); 884 range.popFrontN(4); 885 ret = null; 886 break; 887 case 't': 888 enforceJson(range[1 .. $].startsWith("rue"), "Expected 'true', got '"~range[0 .. min(4, $)]~"'.", filename, line); 889 range.popFrontN(4); 890 ret = true; 891 break; 892 case '0': .. case '9': 893 case '-': 894 bool is_float; 895 auto num = skipNumber(range, is_float, filename, line); 896 if( is_float ) ret = to!double(num); 897 else ret = to!long(num); 898 break; 899 case '\"': 900 ret = skipJsonString(range, filename, line); 901 break; 902 case '[': 903 Json[] arr; 904 range.popFront(); 905 while (true) { 906 skipWhitespace(range, line); 907 enforceJson(!range.empty, "Missing ']' before EOF.", filename, line); 908 if(range.front == ']') break; 909 arr ~= parseJson(range, line, filename); 910 skipWhitespace(range, line); 911 enforceJson(!range.empty, "Missing ']' before EOF.", filename, line); 912 enforceJson(range.front == ',' || range.front == ']', 913 format("Expected ']' or ',' - got '%s'.", range.front), filename, line); 914 if( range.front == ']' ) break; 915 else range.popFront(); 916 } 917 range.popFront(); 918 ret = arr; 919 break; 920 case '{': 921 Json[string] obj; 922 range.popFront(); 923 while (true) { 924 skipWhitespace(range, line); 925 enforceJson(!range.empty, "Missing '}' before EOF.", filename, line); 926 if(range.front == '}') break; 927 string key = skipJsonString(range, filename, line); 928 skipWhitespace(range, line); 929 enforceJson(range.startsWith(":"), "Expected ':' for key '" ~ key ~ "'", filename, line); 930 range.popFront(); 931 skipWhitespace(range, line); 932 Json itm = parseJson(range, line, filename); 933 obj[key] = itm; 934 skipWhitespace(range, line); 935 enforceJson(!range.empty, "Missing '}' before EOF.", filename, line); 936 enforceJson(range.front == ',' || range.front == '}', 937 format("Expected '}' or ',' - got '%s'.", range.front), filename, line); 938 if (range.front == '}') break; 939 else range.popFront(); 940 } 941 range.popFront(); 942 ret = obj; 943 break; 944 default: 945 enforceJson(false, format("Expected valid JSON token, got '%s'.", range[0 .. min(12, $)]), filename, line); 946 assert(false); 947 } 948 949 assert(ret.type != Json.Type.undefined); 950 version(JsonLineNumbers) ret.line = curline; 951 ret.m_fileName = filename; 952 return ret; 953 } 954 955 /** 956 Parses the given JSON string and returns the corresponding Json object. 957 958 Throws a JSONException if any parsing error occurs. 959 */ 960 Json parseJsonString(string str, string filename = null) 961 { 962 import std..string : strip; 963 964 auto strcopy = str; 965 int line = 0; 966 auto ret = parseJson(strcopy, &line, filename); 967 enforceJson(strcopy.strip().length == 0, "Expected end of string after JSON value.", filename, line); 968 return ret; 969 } 970 971 unittest { 972 assert(parseJsonString("null") == Json(null)); 973 assert(parseJsonString("true") == Json(true)); 974 assert(parseJsonString("false") == Json(false)); 975 assert(parseJsonString("1") == Json(1)); 976 assert(parseJsonString("2.0") == Json(2.0)); 977 assert(parseJsonString("\"test\"") == Json("test")); 978 assert(parseJsonString("[1, 2, 3]") == Json([Json(1), Json(2), Json(3)])); 979 assert(parseJsonString("{\"a\": 1}") == Json(["a": Json(1)])); 980 assert(parseJsonString(`"\\\/\b\f\n\r\t\u1234"`).get!string == "\\/\b\f\n\r\t\u1234"); 981 auto json = parseJsonString(`{"hey": "This is @à test éhééhhéhéé !%/??*&?\ud83d\udcec"}`); 982 assert(json.toPrettyString() == parseJsonString(json.toPrettyString()).toPrettyString()); 983 } 984 985 unittest { 986 import std..string : endsWith; 987 988 try parseJsonString(`{"a": 1`); 989 catch (Exception e) assert(e.msg.endsWith("Missing '}' before EOF.")); 990 try parseJsonString(`{"a": 1 x`); 991 catch (Exception e) assert(e.msg.endsWith("Expected '}' or ',' - got 'x'.")); 992 try parseJsonString(`[1`); 993 catch (Exception e) assert(e.msg.endsWith("Missing ']' before EOF.")); 994 try parseJsonString(`[1 x`); 995 catch (Exception e) assert(e.msg.endsWith("Expected ']' or ',' - got 'x'.")); 996 } 997 998 /** 999 Serializes the given value to JSON. 1000 1001 The following types of values are supported: 1002 1003 $(DL 1004 $(DT `Json`) $(DD Used as-is) 1005 $(DT `null`) $(DD Converted to `Json.Type.null_`) 1006 $(DT `bool`) $(DD Converted to `Json.Type.bool_`) 1007 $(DT `float`, `double`) $(DD Converted to `Json.Type.float_`) 1008 $(DT `short`, `ushort`, `int`, `uint`, `long`, `ulong`) $(DD Converted to `Json.Type.int_`) 1009 $(DT `string`) $(DD Converted to `Json.Type.string`) 1010 $(DT `T[]`) $(DD Converted to `Json.Type.array`) 1011 $(DT `T[string]`) $(DD Converted to `Json.Type.object`) 1012 $(DT `struct`) $(DD Converted to `Json.Type.object`) 1013 $(DT `class`) $(DD Converted to `Json.Type.object` or `Json.Type.null_`) 1014 ) 1015 1016 All entries of an array or an associative array, as well as all R/W properties and 1017 all public fields of a struct/class are recursively serialized using the same rules. 1018 1019 Fields ending with an underscore will have the last underscore stripped in the 1020 serialized output. This makes it possible to use fields with D keywords as their name 1021 by simply appending an underscore. 1022 1023 The following methods can be used to customize the serialization of structs/classes: 1024 1025 --- 1026 Json toJson() const; 1027 static T fromJson(Json src); 1028 1029 string toString() const; 1030 static T fromString(string src); 1031 --- 1032 1033 The methods will have to be defined in pairs. The first pair that is implemented by 1034 the type will be used for serialization (i.e. `toJson` overrides `toString`). 1035 1036 See_Also: `deserializeJson`, `vibe.data.serialization` 1037 */ 1038 Json serializeToJson(T)(T value) 1039 { 1040 version (VibeOldSerialization) { 1041 return serializeToJsonOld(value); 1042 } else { 1043 return serialize!JsonSerializer(value); 1044 } 1045 } 1046 /// ditto 1047 void serializeToJson(R, T)(R destination, T value) 1048 if (isOutputRange!(R, char) || isOutputRange!(R, ubyte)) 1049 { 1050 serialize!(JsonStringSerializer!R)(value, destination); 1051 } 1052 /// ditto 1053 string serializeToJsonString(T)(T value) 1054 { 1055 auto ret = appender!string; 1056 serializeToJson(ret, value); 1057 return ret.data; 1058 } 1059 1060 /// 1061 unittest { 1062 struct Foo { 1063 int number; 1064 string str; 1065 } 1066 1067 Foo f; 1068 f.number = 12; 1069 f.str = "hello"; 1070 1071 string json = serializeToJsonString(f); 1072 assert(json == `{"number":12,"str":"hello"}`); 1073 1074 Json jsonval = serializeToJson(f); 1075 assert(jsonval.type == Json.Type.object); 1076 assert(jsonval["number"] == Json(12)); 1077 assert(jsonval["str"] == Json("hello")); 1078 } 1079 1080 1081 /** 1082 Serializes the given value to a pretty printed JSON string. 1083 1084 See_also: `serializeToJson`, `vibe.data.serialization` 1085 */ 1086 void serializeToPrettyJson(R, T)(R destination, T value) 1087 if (isOutputRange!(R, char) || isOutputRange!(R, ubyte)) 1088 { 1089 serialize!(JsonStringSerializer!(R, true))(value, destination); 1090 } 1091 /// ditto 1092 string serializeToPrettyJson(T)(T value) 1093 { 1094 auto ret = appender!string; 1095 serializeToPrettyJson(ret, value); 1096 return ret.data; 1097 } 1098 1099 /// 1100 unittest { 1101 struct Foo { 1102 int number; 1103 string str; 1104 } 1105 1106 Foo f; 1107 f.number = 12; 1108 f.str = "hello"; 1109 1110 string json = serializeToPrettyJson(f); 1111 assert(json == 1112 `{ 1113 "number": 12, 1114 "str": "hello" 1115 }`); 1116 } 1117 1118 1119 /// private 1120 Json serializeToJsonOld(T)(T value) 1121 { 1122 import vibe.internal.meta.traits; 1123 1124 alias TU = Unqual!T; 1125 static if (is(TU == Json)) return value; 1126 else static if (is(TU == typeof(null))) return Json(null); 1127 else static if (is(TU == bool)) return Json(value); 1128 else static if (is(TU == float)) return Json(cast(double)value); 1129 else static if (is(TU == double)) return Json(value); 1130 else static if (is(TU == DateTime)) return Json(value.toISOExtString()); 1131 else static if (is(TU == SysTime)) return Json(value.toISOExtString()); 1132 else static if (is(TU == Date)) return Json(value.toISOExtString()); 1133 else static if (is(TU : long)) return Json(cast(long)value); 1134 else static if (is(TU : string)) return Json(value); 1135 else static if (isArray!T) { 1136 auto ret = new Json[value.length]; 1137 foreach (i; 0 .. value.length) 1138 ret[i] = serializeToJson(value[i]); 1139 return Json(ret); 1140 } else static if (isAssociativeArray!TU) { 1141 Json[string] ret; 1142 alias TK = KeyType!T; 1143 foreach (key, value; value) { 1144 static if(is(TK == string)) { 1145 ret[key] = serializeToJson(value); 1146 } else static if (is(TK == enum)) { 1147 ret[to!string(key)] = serializeToJson(value); 1148 } else static if (isStringSerializable!(TK)) { 1149 ret[key.toString()] = serializeToJson(value); 1150 } else static assert("AA key type %s not supported for JSON serialization."); 1151 } 1152 return Json(ret); 1153 } else static if (isJsonSerializable!TU) { 1154 return value.toJson(); 1155 } else static if (isStringSerializable!TU) { 1156 return Json(value.toString()); 1157 } else static if (is(TU == struct)) { 1158 Json[string] ret; 1159 foreach (m; __traits(allMembers, T)) { 1160 static if (isRWField!(TU, m)) { 1161 auto mv = __traits(getMember, value, m); 1162 ret[underscoreStrip(m)] = serializeToJson(mv); 1163 } 1164 } 1165 return Json(ret); 1166 } else static if(is(TU == class)) { 1167 if (value is null) return Json(null); 1168 Json[string] ret; 1169 foreach (m; __traits(allMembers, T)) { 1170 static if (isRWField!(TU, m)) { 1171 auto mv = __traits(getMember, value, m); 1172 ret[underscoreStrip(m)] = serializeToJson(mv); 1173 } 1174 } 1175 return Json(ret); 1176 } else static if (isPointer!TU) { 1177 if (value is null) return Json(null); 1178 return serializeToJson(*value); 1179 } else { 1180 static assert(false, "Unsupported type '"~T.stringof~"' for JSON serialization."); 1181 } 1182 } 1183 1184 1185 /** 1186 Deserializes a JSON value into the destination variable. 1187 1188 The same types as for `serializeToJson()` are supported and handled inversely. 1189 1190 See_Also: `serializeToJson`, `serializeToJsonString`, `vibe.data.serialization` 1191 */ 1192 void deserializeJson(T)(ref T dst, Json src) 1193 { 1194 dst = deserializeJson!T(src); 1195 } 1196 /// ditto 1197 T deserializeJson(T)(Json src) 1198 { 1199 version (VibeOldSerialization) { 1200 return deserializeJsonOld!T(src); 1201 } else { 1202 return deserialize!(JsonSerializer, T)(src); 1203 } 1204 } 1205 /// ditto 1206 T deserializeJson(T, R)(R input) 1207 if (isInputRange!R && !is(R == Json)) 1208 { 1209 return deserialize!(JsonStringSerializer!R, T)(input); 1210 } 1211 1212 /// private 1213 T deserializeJsonOld(T)(Json src) 1214 { 1215 import vibe.internal.meta.traits; 1216 1217 static if( is(T == struct) || isSomeString!T || isIntegral!T || isFloatingPoint!T ) 1218 if( src.type == Json.Type.null_ ) return T.init; 1219 static if (is(T == Json)) return src; 1220 else static if (is(T == typeof(null))) { return null; } 1221 else static if (is(T == bool)) return src.get!bool; 1222 else static if (is(T == float)) return src.to!float; // since doubles are frequently serialized without 1223 else static if (is(T == double)) return src.to!double; // a decimal point, we allow conversions here 1224 else static if (is(T == DateTime)) return DateTime.fromISOExtString(src.get!string); 1225 else static if (is(T == SysTime)) return SysTime.fromISOExtString(src.get!string); 1226 else static if (is(T == Date)) return Date.fromISOExtString(src.get!string); 1227 else static if (is(T : long)) return cast(T)src.get!long; 1228 else static if (is(T : string)) return cast(T)src.get!string; 1229 else static if (isArray!T) { 1230 alias TV = typeof(T.init[0]) ; 1231 auto dst = new Unqual!TV[src.length]; 1232 foreach (size_t i, v; src) 1233 dst[i] = deserializeJson!(Unqual!TV)(v); 1234 return cast(T)dst; 1235 } else static if( isAssociativeArray!T ) { 1236 alias TV = typeof(T.init.values[0]) ; 1237 alias TK = KeyType!T; 1238 Unqual!TV[TK] dst; 1239 foreach (string key, value; src) { 1240 static if (is(TK == string)) { 1241 dst[key] = deserializeJson!(Unqual!TV)(value); 1242 } else static if (is(TK == enum)) { 1243 dst[to!(TK)(key)] = deserializeJson!(Unqual!TV)(value); 1244 } else static if (isStringSerializable!TK) { 1245 auto dsk = TK.fromString(key); 1246 dst[dsk] = deserializeJson!(Unqual!TV)(value); 1247 } else static assert("AA key type %s not supported for JSON serialization."); 1248 } 1249 return dst; 1250 } else static if (isJsonSerializable!T) { 1251 return T.fromJson(src); 1252 } else static if (isStringSerializable!T) { 1253 return T.fromString(src.get!string); 1254 } else static if (is(T == struct)) { 1255 T dst; 1256 foreach (m; __traits(allMembers, T)) { 1257 static if (isRWPlainField!(T, m) || isRWField!(T, m)) { 1258 alias TM = typeof(__traits(getMember, dst, m)) ; 1259 __traits(getMember, dst, m) = deserializeJson!TM(src[underscoreStrip(m)]); 1260 } 1261 } 1262 return dst; 1263 } else static if (is(T == class)) { 1264 if (src.type == Json.Type.null_) return null; 1265 auto dst = new T; 1266 foreach (m; __traits(allMembers, T)) { 1267 static if (isRWPlainField!(T, m) || isRWField!(T, m)) { 1268 alias TM = typeof(__traits(getMember, dst, m)) ; 1269 __traits(getMember, dst, m) = deserializeJson!TM(src[underscoreStrip(m)]); 1270 } 1271 } 1272 return dst; 1273 } else static if (isPointer!T) { 1274 if (src.type == Json.Type.null_) return null; 1275 alias TD = typeof(*T.init) ; 1276 dst = new TD; 1277 *dst = deserializeJson!TD(src); 1278 return dst; 1279 } else { 1280 static assert(false, "Unsupported type '"~T.stringof~"' for JSON serialization."); 1281 } 1282 } 1283 1284 /// 1285 unittest { 1286 struct Foo { 1287 int number; 1288 string str; 1289 } 1290 1291 Foo f = deserializeJson!Foo(`{"number": 12, "str": "hello"}`); 1292 assert(f.number == 12); 1293 assert(f.str == "hello"); 1294 } 1295 1296 unittest { 1297 import std.stdio; 1298 enum Foo : string { k = "test" } 1299 enum Boo : int { l = 5 } 1300 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; } 1301 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}; 1302 S u; 1303 deserializeJson(u, serializeToJson(t)); 1304 assert(t.a == u.a); 1305 assert(t.b == u.b); 1306 assert(t.c == u.c); 1307 assert(t.d == u.d); 1308 assert(t.e == u.e); 1309 assert(t.f == u.f); 1310 assert(t.g == u.g); 1311 assert(t.h == u.h); 1312 assert(t.i == u.i); 1313 assert(t.j == u.j); 1314 assert(t.k == u.k); 1315 assert(t.l == u.l); 1316 } 1317 1318 unittest 1319 { 1320 assert(uint.max == serializeToJson(uint.max).deserializeJson!uint); 1321 assert(ulong.max == serializeToJson(ulong.max).deserializeJson!ulong); 1322 } 1323 1324 unittest { 1325 static struct A { int value; static A fromJson(Json val) { return A(val.get!int); } Json toJson() const { return Json(value); } } 1326 static struct C { int value; static C fromString(string val) { return C(val.to!int); } string toString() const { return value.to!string; } } 1327 static struct D { int value; } 1328 1329 assert(serializeToJson(const A(123)) == Json(123)); 1330 assert(serializeToJson(A(123)) == Json(123)); 1331 assert(serializeToJson(const C(123)) == Json("123")); 1332 assert(serializeToJson(C(123)) == Json("123")); 1333 assert(serializeToJson(const D(123)) == serializeToJson(["value": 123])); 1334 assert(serializeToJson(D(123)) == serializeToJson(["value": 123])); 1335 } 1336 1337 unittest { 1338 auto d = Date(2001,1,1); 1339 deserializeJson(d, serializeToJson(Date.init)); 1340 assert(d == Date.init); 1341 deserializeJson(d, serializeToJson(Date(2001,1,1))); 1342 assert(d == Date(2001,1,1)); 1343 struct S { immutable(int)[] x; } 1344 S s; 1345 deserializeJson(s, serializeToJson(S([1,2,3]))); 1346 assert(s == S([1,2,3])); 1347 struct T { 1348 @optional S s; 1349 @optional int i; 1350 @optional float f_; // underscore strip feature 1351 @optional double d; 1352 @optional string str; 1353 } 1354 auto t = T(S([1,2,3])); 1355 deserializeJson(t, parseJsonString(`{ "s" : null, "i" : null, "f" : null, "d" : null, "str" : null }`)); 1356 assert(text(t) == text(T())); 1357 } 1358 1359 unittest { 1360 static class C { 1361 int a; 1362 private int _b; 1363 @property int b() const { return _b; } 1364 @property void b(int v) { _b = v; } 1365 1366 @property int test() const { return 10; } 1367 1368 void test2() {} 1369 } 1370 C c = new C; 1371 c.a = 1; 1372 c.b = 2; 1373 1374 C d; 1375 deserializeJson(d, serializeToJson(c)); 1376 assert(c.a == d.a); 1377 assert(c.b == d.b); 1378 } 1379 1380 unittest { 1381 static struct C { int value; static C fromString(string val) { return C(val.to!int); } string toString() const { return value.to!string; } } 1382 enum Color { Red, Green, Blue } 1383 { 1384 static class T { 1385 string[Color] enumIndexedMap; 1386 string[C] stringableIndexedMap; 1387 this() { 1388 enumIndexedMap = [ Color.Red : "magenta", Color.Blue : "deep blue" ]; 1389 stringableIndexedMap = [ C(42) : "forty-two" ]; 1390 } 1391 } 1392 1393 T original = new T; 1394 original.enumIndexedMap[Color.Green] = "olive"; 1395 T other; 1396 deserializeJson(other, serializeToJson(original)); 1397 assert(serializeToJson(other) == serializeToJson(original)); 1398 } 1399 { 1400 static struct S { 1401 string[Color] enumIndexedMap; 1402 string[C] stringableIndexedMap; 1403 } 1404 1405 S *original = new S; 1406 original.enumIndexedMap = [ Color.Red : "magenta", Color.Blue : "deep blue" ]; 1407 original.enumIndexedMap[Color.Green] = "olive"; 1408 original.stringableIndexedMap = [ C(42) : "forty-two" ]; 1409 S other; 1410 deserializeJson(other, serializeToJson(original)); 1411 assert(serializeToJson(other) == serializeToJson(original)); 1412 } 1413 } 1414 1415 unittest { 1416 import std.typecons : Nullable; 1417 1418 struct S { Nullable!int a, b; } 1419 S s; 1420 s.a = 2; 1421 1422 auto j = serializeToJson(s); 1423 assert(j["a"].type == Json.Type.int_); 1424 assert(j["b"].type == Json.Type.null_); 1425 1426 auto t = deserializeJson!S(j); 1427 assert(!t.a.isNull() && t.a == 2); 1428 assert(t.b.isNull()); 1429 } 1430 1431 unittest { // #840 1432 int[2][2] nestedArray = 1; 1433 assert(nestedArray.serializeToJson.deserializeJson!(typeof(nestedArray)) == nestedArray); 1434 } 1435 1436 1437 /** 1438 Serializer for a plain Json representation. 1439 1440 See_Also: vibe.data.serialization.serialize, vibe.data.serialization.deserialize, serializeToJson, deserializeJson 1441 */ 1442 struct JsonSerializer { 1443 template isJsonBasicType(T) { enum isJsonBasicType = isNumeric!T || isBoolean!T || is(T == string) || is(T == typeof(null)) || isJsonSerializable!T; } 1444 1445 template isSupportedValueType(T) { enum isSupportedValueType = isJsonBasicType!T || is(T == Json); } 1446 1447 private { 1448 Json m_current; 1449 Json[] m_compositeStack; 1450 } 1451 1452 this(Json data) { m_current = data; } 1453 1454 @disable this(this); 1455 1456 // 1457 // serialization 1458 // 1459 Json getSerializedResult() { return m_current; } 1460 void beginWriteDictionary(T)() { m_compositeStack ~= Json.emptyObject; } 1461 void endWriteDictionary(T)() { m_current = m_compositeStack[$-1]; m_compositeStack.length--; } 1462 void beginWriteDictionaryEntry(T)(string name) {} 1463 void endWriteDictionaryEntry(T)(string name) { m_compositeStack[$-1][name] = m_current; } 1464 1465 void beginWriteArray(T)(size_t) { m_compositeStack ~= Json.emptyArray; } 1466 void endWriteArray(T)() { m_current = m_compositeStack[$-1]; m_compositeStack.length--; } 1467 void beginWriteArrayEntry(T)(size_t) {} 1468 void endWriteArrayEntry(T)(size_t) { m_compositeStack[$-1].appendArrayElement(m_current); } 1469 1470 void writeValue(T)(T value) 1471 { 1472 static if (is(T == Json)) m_current = value; 1473 else static if (isJsonSerializable!T) m_current = value.toJson(); 1474 else m_current = Json(value); 1475 } 1476 1477 void writeValue(T)(in Json value) if (is(T == Json)) 1478 { 1479 m_current = value.clone; 1480 } 1481 1482 // 1483 // deserialization 1484 // 1485 void readDictionary(T)(scope void delegate(string) field_handler) 1486 { 1487 enforceJson(m_current.type == Json.Type.object, "Expected JSON object, got "~m_current.type.to!string); 1488 auto old = m_current; 1489 foreach (string key, value; m_current) { 1490 m_current = value; 1491 field_handler(key); 1492 } 1493 m_current = old; 1494 } 1495 1496 void readArray(T)(scope void delegate(size_t) size_callback, scope void delegate() entry_callback) 1497 { 1498 enforceJson(m_current.type == Json.Type.array, "Expected JSON array, got "~m_current.type.to!string); 1499 auto old = m_current; 1500 size_callback(m_current.length); 1501 foreach (ent; old) { 1502 m_current = ent; 1503 entry_callback(); 1504 } 1505 m_current = old; 1506 } 1507 1508 T readValue(T)() 1509 { 1510 static if (is(T == Json)) return m_current; 1511 else static if (isJsonSerializable!T) return T.fromJson(m_current); 1512 else static if (is(T == float) || is(T == double)) { 1513 if (m_current.type == Json.Type.undefined) return T.nan; 1514 return m_current.type == Json.Type.float_ ? cast(T)m_current.get!double : cast(T)m_current.get!long; 1515 } 1516 else { 1517 return m_current.get!T(); 1518 } 1519 } 1520 1521 bool tryReadNull() { return m_current.type == Json.Type.null_; } 1522 } 1523 1524 1525 /** 1526 Serializer for a range based plain JSON string representation. 1527 1528 See_Also: vibe.data.serialization.serialize, vibe.data.serialization.deserialize, serializeToJson, deserializeJson 1529 */ 1530 struct JsonStringSerializer(R, bool pretty = false) 1531 if (isInputRange!R || isOutputRange!(R, char)) 1532 { 1533 private { 1534 R m_range; 1535 size_t m_level = 0; 1536 } 1537 1538 template isJsonBasicType(T) { enum isJsonBasicType = isNumeric!T || isBoolean!T || is(T == string) || is(T == typeof(null)) || isJsonSerializable!T; } 1539 1540 template isSupportedValueType(T) { enum isSupportedValueType = isJsonBasicType!T || is(T == Json); } 1541 1542 this(R range) 1543 { 1544 m_range = range; 1545 } 1546 1547 @disable this(this); 1548 1549 // 1550 // serialization 1551 // 1552 static if (isOutputRange!(R, char)) { 1553 private { 1554 bool m_firstInComposite; 1555 } 1556 1557 void getSerializedResult() {} 1558 1559 void beginWriteDictionary(T)() { startComposite(); m_range.put('{'); } 1560 void endWriteDictionary(T)() { endComposite(); m_range.put("}"); } 1561 void beginWriteDictionaryEntry(T)(string name) 1562 { 1563 startCompositeEntry(); 1564 m_range.put('"'); 1565 m_range.jsonEscape(name); 1566 static if (pretty) m_range.put(`": `); 1567 else m_range.put(`":`); 1568 } 1569 void endWriteDictionaryEntry(T)(string name) {} 1570 1571 void beginWriteArray(T)(size_t) { startComposite(); m_range.put('['); } 1572 void endWriteArray(T)() { endComposite(); m_range.put(']'); } 1573 void beginWriteArrayEntry(T)(size_t) { startCompositeEntry(); } 1574 void endWriteArrayEntry(T)(size_t) {} 1575 1576 void writeValue(T)(in T value) 1577 { 1578 static if (is(T == typeof(null))) m_range.put("null"); 1579 else static if (is(T == bool)) m_range.put(value ? "true" : "false"); 1580 else static if (is(T : long)) m_range.formattedWrite("%s", value); 1581 else static if (is(T : real)) m_range.formattedWrite("%.16g", value); 1582 else static if (is(T == string)) { 1583 m_range.put('"'); 1584 m_range.jsonEscape(value); 1585 m_range.put('"'); 1586 } 1587 else static if (is(T == Json)) m_range.writeJsonString(value); 1588 else static if (isJsonSerializable!T) m_range.writeJsonString!(R, pretty)(value.toJson(), m_level); 1589 else static assert(false, "Unsupported type: " ~ T.stringof); 1590 } 1591 1592 private void startComposite() 1593 { 1594 static if (pretty) m_level++; 1595 m_firstInComposite = true; 1596 } 1597 1598 private void startCompositeEntry() 1599 { 1600 if (!m_firstInComposite) { 1601 m_range.put(','); 1602 } else { 1603 m_firstInComposite = false; 1604 } 1605 static if (pretty) indent(); 1606 } 1607 1608 private void endComposite() 1609 { 1610 static if (pretty) { 1611 m_level--; 1612 if (!m_firstInComposite) indent(); 1613 } 1614 m_firstInComposite = false; 1615 } 1616 1617 private void indent() 1618 { 1619 m_range.put('\n'); 1620 foreach (i; 0 .. m_level) m_range.put('\t'); 1621 } 1622 } 1623 1624 // 1625 // deserialization 1626 // 1627 static if (isInputRange!(R)) { 1628 private { 1629 int m_line = 0; 1630 } 1631 1632 void readDictionary(T)(scope void delegate(string) entry_callback) 1633 { 1634 m_range.skipWhitespace(&m_line); 1635 enforceJson(!m_range.empty && m_range.front == '{', "Expecting object."); 1636 m_range.popFront(); 1637 bool first = true; 1638 while(true) { 1639 m_range.skipWhitespace(&m_line); 1640 enforceJson(!m_range.empty, "Missing '}'."); 1641 if (m_range.front == '}') { 1642 m_range.popFront(); 1643 break; 1644 } else if (!first) { 1645 enforceJson(m_range.front == ',', "Expecting ',' or '}', not '"~m_range.front.to!string~"'."); 1646 m_range.popFront(); 1647 m_range.skipWhitespace(&m_line); 1648 } else first = false; 1649 1650 auto name = m_range.skipJsonString(null, &m_line); 1651 1652 m_range.skipWhitespace(&m_line); 1653 enforceJson(!m_range.empty && m_range.front == ':', "Expecting ':', not '"~m_range.front.to!string~"'."); 1654 m_range.popFront(); 1655 1656 entry_callback(name); 1657 } 1658 } 1659 1660 void readArray(T)(scope void delegate(size_t) size_callback, scope void delegate() entry_callback) 1661 { 1662 m_range.skipWhitespace(&m_line); 1663 enforceJson(!m_range.empty && m_range.front == '[', "Expecting array."); 1664 m_range.popFront(); 1665 bool first = true; 1666 while(true) { 1667 m_range.skipWhitespace(&m_line); 1668 enforceJson(!m_range.empty, "Missing ']'."); 1669 if (m_range.front == ']') { 1670 m_range.popFront(); 1671 break; 1672 } else if (!first) { 1673 enforceJson(m_range.front == ',', "Expecting ',' or ']'."); 1674 m_range.popFront(); 1675 } else first = false; 1676 1677 entry_callback(); 1678 } 1679 } 1680 1681 T readValue(T)() 1682 { 1683 m_range.skipWhitespace(&m_line); 1684 static if (is(T == typeof(null))) { enforceJson(m_range.take(4).equal("null"), "Expecting 'null'."); return null; } 1685 else static if (is(T == bool)) { 1686 bool ret = m_range.front == 't'; 1687 string expected = ret ? "true" : "false"; 1688 foreach (ch; expected) { 1689 enforceJson(m_range.front == ch, "Expecting 'true' or 'false'."); 1690 m_range.popFront(); 1691 } 1692 return ret; 1693 } else static if (is(T : long)) { 1694 bool is_float; 1695 auto num = m_range.skipNumber(is_float, null, &m_line); 1696 enforceJson(!is_float, "Expecting integer number."); 1697 return to!T(num); 1698 } else static if (is(T : real)) { 1699 bool is_float; 1700 auto num = m_range.skipNumber(is_float); 1701 return to!T(num); 1702 } 1703 else static if (is(T == string)) return m_range.skipJsonString(null, &m_line); 1704 else static if (is(T == Json)) return m_range.parseJson(&m_line); 1705 else static if (isJsonSerializable!T) return T.fromJson(m_range.parseJson(&m_line)); 1706 else static assert(false, "Unsupported type: " ~ T.stringof); 1707 } 1708 1709 bool tryReadNull() 1710 { 1711 m_range.skipWhitespace(&m_line); 1712 if (m_range.front != 'n') return false; 1713 foreach (ch; "null") { 1714 enforceJson(m_range.front == ch, "Expecting 'null'."); 1715 m_range.popFront(); 1716 } 1717 assert(m_range.empty || m_range.front != 'l'); 1718 return true; 1719 } 1720 } 1721 } 1722 1723 1724 1725 /** 1726 Writes the given JSON object as a JSON string into the destination range. 1727 1728 This function will convert the given JSON value to a string without adding 1729 any white space between tokens (no newlines, no indentation and no padding). 1730 The output size is thus minimized, at the cost of bad human readability. 1731 1732 Params: 1733 dst = References the string output range to which the result is written. 1734 json = Specifies the JSON value that is to be stringified. 1735 1736 See_Also: Json.toString, writePrettyJsonString 1737 */ 1738 void writeJsonString(R, bool pretty = false)(ref R dst, in Json json, size_t level = 0) 1739 // if( isOutputRange!R && is(ElementEncodingType!R == char) ) 1740 { 1741 final switch( json.type ){ 1742 case Json.Type.undefined: dst.put("undefined"); break; 1743 case Json.Type.null_: dst.put("null"); break; 1744 case Json.Type.bool_: dst.put(cast(bool)json ? "true" : "false"); break; 1745 case Json.Type.int_: formattedWrite(dst, "%d", json.get!long); break; 1746 case Json.Type.float_: 1747 auto d = json.get!double; 1748 if (d != d) 1749 dst.put("undefined"); // JSON has no NaN value so set null 1750 else 1751 formattedWrite(dst, "%.16g", json.get!double); 1752 break; 1753 case Json.Type..string: 1754 dst.put('\"'); 1755 jsonEscape(dst, cast(string)json); 1756 dst.put('\"'); 1757 break; 1758 case Json.Type.array: 1759 dst.put('['); 1760 bool first = true; 1761 foreach (ref const Json e; json) { 1762 if( !first ) dst.put(","); 1763 first = false; 1764 static if (pretty) { 1765 dst.put('\n'); 1766 foreach (tab; 0 .. level+1) dst.put('\t'); 1767 } 1768 if (e.type == Json.Type.undefined) dst.put("null"); 1769 else writeJsonString!(R, pretty)(dst, e, level+1); 1770 } 1771 static if (pretty) { 1772 if (json.length > 0) { 1773 dst.put('\n'); 1774 foreach (tab; 0 .. level) dst.put('\t'); 1775 } 1776 } 1777 dst.put(']'); 1778 break; 1779 case Json.Type.object: 1780 dst.put('{'); 1781 bool first = true; 1782 1783 static if (pretty) { 1784 import std.algorithm.sorting : sort; 1785 string[] keyOrder; 1786 foreach (string key, ref const Json e; json) keyOrder ~= key; 1787 keyOrder.sort(); 1788 1789 foreach( key; keyOrder ){ 1790 if( json[key].type == Json.Type.undefined ) continue; 1791 if( !first ) dst.put(','); 1792 first = false; 1793 dst.put('\n'); 1794 foreach (tab; 0 .. level+1) dst.put('\t'); 1795 dst.put('\"'); 1796 jsonEscape(dst, key); 1797 dst.put(pretty ? `": ` : `":`); 1798 writeJsonString!(R, pretty)(dst, json[key], level+1); 1799 } 1800 if (json.length > 0) { 1801 dst.put('\n'); 1802 foreach (tab; 0 .. level) dst.put('\t'); 1803 } 1804 } else { 1805 foreach( string k, ref const Json e; json ){ 1806 if( e.type == Json.Type.undefined ) continue; 1807 if( !first ) dst.put(','); 1808 first = false; 1809 dst.put('\"'); 1810 jsonEscape(dst, k); 1811 dst.put(pretty ? `": ` : `":`); 1812 writeJsonString!(R, pretty)(dst, e, level+1); 1813 } 1814 } 1815 dst.put('}'); 1816 break; 1817 } 1818 } 1819 1820 unittest { 1821 auto a = Json.emptyObject; 1822 a["a"] = Json.emptyArray; 1823 a["b"] = Json.emptyArray; 1824 a["b"] ~= Json(1); 1825 a["b"] ~= Json.emptyObject; 1826 1827 assert(a.toString() == `{"a":[],"b":[1,{}]}` || a.toString == `{"b":[1,{}],"a":[]}`); 1828 assert(a.toPrettyString() == 1829 `{ 1830 "a": [], 1831 "b": [ 1832 1, 1833 {} 1834 ] 1835 }`); 1836 } 1837 1838 unittest { // #735 1839 auto a = Json.emptyArray; 1840 a ~= "a"; 1841 a ~= Json(); 1842 a ~= "b"; 1843 a ~= null; 1844 a ~= "c"; 1845 assert(a.toString() == `["a",null,"b",null,"c"]`); 1846 } 1847 1848 unittest { 1849 auto a = Json.emptyArray; 1850 a ~= Json(1); 1851 a ~= Json(2); 1852 a ~= Json(3); 1853 a ~= Json(4); 1854 a ~= Json(5); 1855 1856 auto b = Json(a[0..a.length]); 1857 assert(a == b); 1858 1859 auto c = Json(a[0..$]); 1860 assert(a == c); 1861 assert(b == c); 1862 1863 auto d = [Json(1),Json(2),Json(3)]; 1864 assert(d == a[0..a.length-2]); 1865 assert(d == a[0..$-2]); 1866 } 1867 1868 unittest { 1869 auto j = Json(double.init); 1870 1871 assert(j.toString == "undefined"); // A double nan should serialize to undefined 1872 j = 17.04f; 1873 assert(j.toString == "17.04"); // A proper double should serialize correctly 1874 1875 double d; 1876 deserializeJson(d, Json.undefined); // Json.undefined should deserialize to nan 1877 assert(d != d); 1878 } 1879 /** 1880 Writes the given JSON object as a prettified JSON string into the destination range. 1881 1882 The output will contain newlines and indents to make the output human readable. 1883 1884 Params: 1885 dst = References the string output range to which the result is written. 1886 json = Specifies the JSON value that is to be stringified. 1887 level = Specifies the base amount of indentation for the output. Indentation is always 1888 done using tab characters. 1889 1890 See_Also: Json.toPrettyString, writeJsonString 1891 */ 1892 void writePrettyJsonString(R)(ref R dst, in Json json, int level = 0) 1893 // if( isOutputRange!R && is(ElementEncodingType!R == char) ) 1894 { 1895 writeJsonString!(R, true)(dst, json, level); 1896 } 1897 1898 1899 /** 1900 Helper function that escapes all Unicode characters in a JSON string. 1901 */ 1902 string convertJsonToASCII(string json) 1903 { 1904 auto ret = appender!string; 1905 jsonEscape!true(ret, json); 1906 return ret.data; 1907 } 1908 1909 1910 /// private 1911 private void jsonEscape(bool escape_unicode = false, R)(ref R dst, string s) 1912 { 1913 for (size_t pos = 0; pos < s.length; pos++) { 1914 immutable(char) ch = s[pos]; 1915 1916 switch (ch) { 1917 default: 1918 static if (escape_unicode) { 1919 if (ch > 0x20 && ch < 0x80) dst.put(ch); 1920 else { 1921 import std.utf : decode; 1922 char[13] buf; 1923 int len; 1924 dchar codepoint = decode(s, pos); 1925 import core.stdc.stdio : sprintf; 1926 /* codepoint is in BMP */ 1927 if(codepoint < 0x10000) 1928 { 1929 sprintf(&buf[0], "\\u%04X", codepoint); 1930 len = 6; 1931 } 1932 /* not in BMP -> construct a UTF-16 surrogate pair */ 1933 else 1934 { 1935 int first, last; 1936 1937 codepoint -= 0x10000; 1938 first = 0xD800 | ((codepoint & 0xffc00) >> 10); 1939 last = 0xDC00 | (codepoint & 0x003ff); 1940 1941 sprintf(&buf[0], "\\u%04X\\u%04X", first, last); 1942 len = 12; 1943 } 1944 1945 pos -= 1; 1946 foreach (i; 0 .. len) 1947 dst.put(buf[i]); 1948 1949 } 1950 } else { 1951 if (ch < 0x20) dst.formattedWrite("\\u%04X", ch); 1952 else dst.put(ch); 1953 } 1954 break; 1955 case '\\': dst.put("\\\\"); break; 1956 case '\r': dst.put("\\r"); break; 1957 case '\n': dst.put("\\n"); break; 1958 case '\t': dst.put("\\t"); break; 1959 case '\"': dst.put("\\\""); break; 1960 } 1961 } 1962 } 1963 1964 /// private 1965 private string jsonUnescape(R)(ref R range, string filename, int* line) 1966 { 1967 auto ret = appender!string(); 1968 while(!range.empty){ 1969 auto ch = range.front; 1970 switch( ch ){ 1971 case '"': return ret.data; 1972 case '\\': 1973 range.popFront(); 1974 enforceJson(!range.empty, "Unterminated string escape sequence.", filename, line); 1975 switch(range.front){ 1976 default: enforceJson(false, "Invalid string escape sequence.", filename, line); break; 1977 case '"': ret.put('\"'); range.popFront(); break; 1978 case '\\': ret.put('\\'); range.popFront(); break; 1979 case '/': ret.put('/'); range.popFront(); break; 1980 case 'b': ret.put('\b'); range.popFront(); break; 1981 case 'f': ret.put('\f'); range.popFront(); break; 1982 case 'n': ret.put('\n'); range.popFront(); break; 1983 case 'r': ret.put('\r'); range.popFront(); break; 1984 case 't': ret.put('\t'); range.popFront(); break; 1985 case 'u': 1986 1987 dchar decode_unicode_escape() { 1988 enforceJson(range.front == 'u'); 1989 range.popFront(); 1990 dchar uch = 0; 1991 foreach( i; 0 .. 4 ){ 1992 uch *= 16; 1993 enforceJson(!range.empty, "Unicode sequence must be '\\uXXXX'.", filename, line); 1994 auto dc = range.front; 1995 range.popFront(); 1996 1997 if( dc >= '0' && dc <= '9' ) uch += dc - '0'; 1998 else if( dc >= 'a' && dc <= 'f' ) uch += dc - 'a' + 10; 1999 else if( dc >= 'A' && dc <= 'F' ) uch += dc - 'A' + 10; 2000 else enforceJson(false, "Unicode sequence must be '\\uXXXX'.", filename, line); 2001 } 2002 return uch; 2003 } 2004 2005 auto uch = decode_unicode_escape(); 2006 2007 if(0xD800 <= uch && uch <= 0xDBFF) { 2008 /* surrogate pair */ 2009 range.popFront(); // backslash '\' 2010 auto uch2 = decode_unicode_escape(); 2011 enforceJson(0xDC00 <= uch2 && uch2 <= 0xDFFF, "invalid Unicode", filename, line); 2012 { 2013 /* valid second surrogate */ 2014 uch = 2015 ((uch - 0xD800) << 10) + 2016 (uch2 - 0xDC00) + 2017 0x10000; 2018 } 2019 } 2020 ret.put(uch); 2021 break; 2022 } 2023 break; 2024 default: 2025 ret.put(ch); 2026 range.popFront(); 2027 break; 2028 } 2029 } 2030 return ret.data; 2031 } 2032 2033 /// private 2034 private string skipNumber(R)(ref R s, out bool is_float, string filename, int* line) 2035 { 2036 // TODO: make this work with input ranges 2037 size_t idx = 0; 2038 is_float = false; 2039 if (s[idx] == '-') idx++; 2040 if (s[idx] == '0') idx++; 2041 else { 2042 enforceJson(isDigit(s[idx++]), "Digit expected at beginning of number.", filename, line); 2043 while( idx < s.length && isDigit(s[idx]) ) idx++; 2044 } 2045 2046 if( idx < s.length && s[idx] == '.' ){ 2047 idx++; 2048 is_float = true; 2049 while( idx < s.length && isDigit(s[idx]) ) idx++; 2050 } 2051 2052 if( idx < s.length && (s[idx] == 'e' || s[idx] == 'E') ){ 2053 idx++; 2054 is_float = true; 2055 if( idx < s.length && (s[idx] == '+' || s[idx] == '-') ) idx++; 2056 enforceJson( idx < s.length && isDigit(s[idx]), "Expected exponent." ~ s[0 .. idx], filename, line); 2057 idx++; 2058 while( idx < s.length && isDigit(s[idx]) ) idx++; 2059 } 2060 2061 string ret = s[0 .. idx]; 2062 s = s[idx .. $]; 2063 return ret; 2064 } 2065 2066 /// private 2067 private string skipJsonString(R)(ref R s, string filename, int* line) 2068 { 2069 // TODO: count or disallow any newlines inside of the string 2070 enforceJson(!s.empty && s.front == '"', "Expected '\"' to start string.", filename, line); 2071 s.popFront(); 2072 string ret = jsonUnescape(s, filename, line); 2073 enforceJson(!s.empty && s.front == '"', "Expected '\"' to terminate string.", filename, line); 2074 s.popFront(); 2075 return ret; 2076 } 2077 2078 /// private 2079 private void skipWhitespace(R)(ref R s, int* line = null) 2080 { 2081 while (!s.empty) { 2082 switch (s.front) { 2083 default: return; 2084 case ' ', '\t': s.popFront(); break; 2085 case '\n': 2086 s.popFront(); 2087 if (!s.empty && s.front == '\r') s.popFront(); 2088 if (line) (*line)++; 2089 break; 2090 case '\r': 2091 s.popFront(); 2092 if (!s.empty && s.front == '\n') s.popFront(); 2093 if (line) (*line)++; 2094 break; 2095 } 2096 } 2097 } 2098 2099 private bool isDigit(dchar ch) { return ch >= '0' && ch <= '9'; } 2100 2101 private string underscoreStrip(string field_name) 2102 { 2103 if( field_name.length < 1 || field_name[$-1] != '_' ) return field_name; 2104 else return field_name[0 .. $-1]; 2105 } 2106 2107 /// private 2108 package template isJsonSerializable(T) { enum isJsonSerializable = is(typeof(T.init.toJson()) == Json) && is(typeof(T.fromJson(Json())) == T); } 2109 2110 private void enforceJson(string file = __FILE__, size_t line = __LINE__)(bool cond, lazy string message = "JSON exception") 2111 { 2112 static if (__VERSION__ >= 2079) 2113 enforce!JSONException(cond, message, file, line); 2114 else 2115 enforceEx!JSONException(cond, message, file, line); 2116 } 2117 2118 private void enforceJson(string file = __FILE__, size_t line = __LINE__)(bool cond, lazy string message, string err_file, int err_line) 2119 { 2120 auto errmsg() { return format("%s(%s): Error: %s", err_file, err_line+1, message); } 2121 static if (__VERSION__ >= 2079) 2122 enforce!JSONException(cond, errmsg, file, line); 2123 else 2124 enforceEx!JSONException(cond, errmsg, file, line); 2125 } 2126 2127 private void enforceJson(string file = __FILE__, size_t line = __LINE__)(bool cond, lazy string message, string err_file, int* err_line) 2128 { 2129 enforceJson!(file, line)(cond, message, err_file, err_line ? *err_line : -1); 2130 }