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