1 /** 2 * JSON encoding. 3 * 4 * License: 5 * This Source Code Form is subject to the terms of 6 * the Mozilla Public License, v. 2.0. If a copy of 7 * the MPL was not distributed with this file, You 8 * can obtain one at http://mozilla.org/MPL/2.0/. 9 * 10 * Authors: 11 * Vladimir Panteleev <vladimir@thecybershadow.net> 12 */ 13 14 module ae.utils.json; 15 16 import std.exception; 17 import std.string; 18 import std.traits; 19 import std.typecons; 20 21 import ae.utils.exception; 22 import ae.utils.meta; 23 import ae.utils.textout; 24 25 // ************************************************************************ 26 27 struct JsonWriter(Output) 28 { 29 /// You can set this to something to e.g. write to another buffer. 30 Output output; 31 32 /// Write a string literal. 33 private void putString(in char[] s) 34 { 35 // TODO: escape Unicode characters? 36 // TODO: Handle U+2028 and U+2029 ( http://timelessrepo.com/json-isnt-a-javascript-subset ) 37 38 output.put('"'); 39 auto start = s.ptr, p = start, end = start+s.length; 40 41 while (p < end) 42 { 43 auto c = *p++; 44 if (Escapes.escaped[c]) 45 output.put(start[0..p-start-1], Escapes.chars[c]), 46 start = p; 47 } 48 49 output.put(start[0..p-start], '"'); 50 } 51 52 /// Write a value of a simple type. 53 void putValue(T)(T v) 54 { 55 static if (is(T == typeof(null))) 56 return output.put("null"); 57 else 58 static if (is(T : const(char)[])) 59 putString(v); 60 else 61 static if (is(Unqual!T == bool)) 62 return output.put(v ? "true" : "false"); 63 else 64 static if (is(Unqual!T : long)) 65 return .put(output, v); 66 else 67 static if (is(Unqual!T : real)) 68 return output.put(fpToString!T(v)); // TODO: don't allocate 69 else 70 static assert(0, "Don't know how to write " ~ T.stringof); 71 } 72 73 void beginArray() 74 { 75 output.put('['); 76 } 77 78 void endArray() 79 { 80 output.put(']'); 81 } 82 83 void beginObject() 84 { 85 output.put('{'); 86 } 87 88 void endObject() 89 { 90 output.put('}'); 91 } 92 93 void putKey(in char[] key) 94 { 95 putString(key); 96 output.put(':'); 97 } 98 99 void putComma() 100 { 101 output.put(','); 102 } 103 } 104 105 struct PrettyJsonWriter(Output, alias indent = '\t', alias newLine = '\n', alias pad = ' ') 106 { 107 JsonWriter!Output jsonWriter; 108 alias jsonWriter this; 109 110 bool indentPending; 111 uint indentLevel; 112 113 void putIndent() 114 { 115 if (indentPending) 116 { 117 foreach (n; 0..indentLevel) 118 output.put(indent); 119 indentPending = false; 120 } 121 } 122 123 void putNewline() 124 { 125 if (!indentPending) 126 { 127 output.put(newLine); 128 indentPending = true; 129 } 130 } 131 132 void putValue(T)(T v) 133 { 134 putIndent(); 135 jsonWriter.putValue(v); 136 } 137 138 void beginArray() 139 { 140 putIndent(); 141 jsonWriter.beginArray(); 142 indentLevel++; 143 putNewline(); 144 } 145 146 void endArray() 147 { 148 indentLevel--; 149 putNewline(); 150 putIndent(); 151 jsonWriter.endArray(); 152 } 153 154 void beginObject() 155 { 156 putIndent(); 157 jsonWriter.beginObject(); 158 indentLevel++; 159 putNewline(); 160 } 161 162 void endObject() 163 { 164 indentLevel--; 165 putNewline(); 166 putIndent(); 167 jsonWriter.endObject(); 168 } 169 170 void putKey(in char[] key) 171 { 172 putIndent(); 173 putString(key); 174 output.put(pad, ':', pad); 175 } 176 177 void putComma() 178 { 179 jsonWriter.putComma(); 180 putNewline(); 181 } 182 } 183 184 struct CustomJsonSerializer(Writer) 185 { 186 Writer writer; 187 188 void put(T)(T v) 189 { 190 static if (is(T == enum)) 191 put(to!string(v)); 192 else 193 static if (is(T : const(char)[]) || is(Unqual!T : real)) 194 writer.putValue(v); 195 else 196 static if (is(T U : U[])) 197 { 198 writer.beginArray(); 199 if (v.length) 200 { 201 put(v[0]); 202 foreach (i; v[1..$]) 203 { 204 writer.putComma(); 205 put(i); 206 } 207 } 208 writer.endArray(); 209 } 210 else 211 static if (isTuple!T) 212 { 213 // TODO: serialize as object if tuple has names 214 enum N = v.expand.length; 215 static if (N == 0) 216 return; 217 else 218 static if (N == 1) 219 put(v.expand[0]); 220 else 221 { 222 writer.beginArray(); 223 foreach (n; RangeTuple!N) 224 { 225 static if (n) 226 writer.putComma(); 227 put(v.expand[n]); 228 } 229 writer.endArray(); 230 } 231 } 232 else 233 static if (is(typeof(T.init.keys)) && is(typeof(T.init.values)) && is(typeof(T.init.keys[0])==string)) 234 { 235 writer.beginObject(); 236 bool first = true; 237 foreach (key, value; v) 238 { 239 if (!first) 240 writer.putComma(); 241 else 242 first = false; 243 writer.putKey(key); 244 put(value); 245 } 246 writer.endObject(); 247 } 248 else 249 static if (is(T==JSONFragment)) 250 writer.output.put(v.json); 251 else 252 static if (is(T==struct)) 253 { 254 writer.beginObject(); 255 bool first = true; 256 foreach (i, field; v.tupleof) 257 { 258 static if (!doSkipSerialize!(T, v.tupleof[i].stringof[2..$])) 259 { 260 static if (hasAttribute!(JSONOptional, v.tupleof[i])) 261 if (v.tupleof[i] == T.init.tupleof[i]) 262 continue; 263 if (!first) 264 writer.putComma(); 265 else 266 first = false; 267 writer.putKey(getJsonName!(T, v.tupleof[i].stringof[2..$])); 268 put(field); 269 } 270 } 271 writer.endObject(); 272 } 273 else 274 static if (is(typeof(*v))) 275 { 276 if (v) 277 put(*v); 278 else 279 writer.putValue(null); 280 } 281 else 282 static assert(0, "Can't serialize " ~ T.stringof ~ " to JSON"); 283 } 284 } 285 286 alias CustomJsonSerializer!(JsonWriter!StringBuilder) JsonSerializer; 287 288 private struct Escapes 289 { 290 static __gshared string[256] chars; 291 static __gshared bool[256] escaped; 292 293 shared static this() 294 { 295 import std.string; 296 297 escaped[] = true; 298 foreach (c; 0..256) 299 if (c=='\\') 300 chars[c] = `\\`; 301 else 302 if (c=='\"') 303 chars[c] = `\"`; 304 else 305 if (c=='\b') 306 chars[c] = `\b`; 307 else 308 if (c=='\f') 309 chars[c] = `\f`; 310 else 311 if (c=='\n') 312 chars[c] = `\n`; 313 else 314 if (c=='\r') 315 chars[c] = `\r`; 316 else 317 if (c=='\t') 318 chars[c] = `\t`; 319 else 320 if (c<'\x20' || c == '\x7F' || c=='<' || c=='>' || c=='&') 321 chars[c] = format(`\u%04x`, c); 322 else 323 chars[c] = [cast(char)c], 324 escaped[c] = false; 325 } 326 } 327 328 // ************************************************************************ 329 330 string toJson(T)(T v) 331 { 332 JsonSerializer serializer; 333 serializer.put(v); 334 return serializer.writer.output.get(); 335 } 336 337 unittest 338 { 339 struct X { int a; string b; } 340 X x = {17, "aoeu"}; 341 assert(toJson(x) == `{"a":17,"b":"aoeu"}`, toJson(x)); 342 int[] arr = [1,5,7]; 343 assert(toJson(arr) == `[1,5,7]`); 344 assert(toJson(true) == `true`); 345 346 assert(toJson(tuple()) == ``); 347 assert(toJson(tuple(42)) == `42`); 348 assert(toJson(tuple(42, "banana")) == `[42,"banana"]`); 349 } 350 351 // ************************************************************************ 352 353 string toPrettyJson(T)(T v) 354 { 355 CustomJsonSerializer!(PrettyJsonWriter!StringBuilder) serializer; 356 serializer.put(v); 357 return serializer.writer.output.get(); 358 } 359 360 unittest 361 { 362 struct X { int a; string b; int[] c, d; } 363 X x = {17, "aoeu", [1, 2, 3]}; 364 assert(toPrettyJson(x) == 365 `{ 366 "a" : 17, 367 "b" : "aoeu", 368 "c" : [ 369 1, 370 2, 371 3 372 ], 373 "d" : [ 374 ] 375 }`, toPrettyJson(x)); 376 } 377 378 // ************************************************************************ 379 380 import std.ascii; 381 import std.utf; 382 import std.conv; 383 384 import ae.utils.text; 385 386 private struct JsonParser(C) 387 { 388 C[] s; 389 size_t p; 390 391 char next() 392 { 393 enforce(p < s.length, "Out of data while parsing JSON stream"); 394 return s[p++]; 395 } 396 397 string readN(uint n) 398 { 399 string r; 400 for (int i=0; i<n; i++) 401 r ~= next(); 402 return r; 403 } 404 405 char peek() 406 { 407 enforce(p < s.length, "Out of data while parsing JSON stream"); 408 return s[p]; 409 } 410 411 @property bool eof() { return p == s.length; } 412 413 void skipWhitespace() 414 { 415 while (isWhite(peek())) 416 p++; 417 } 418 419 void expect(char c) 420 { 421 auto n = next(); 422 enforce(n==c, "Expected " ~ c ~ ", got " ~ n); 423 } 424 425 T read(T)() 426 { 427 static if (is(T X == Nullable!X)) 428 return readNullable!X(); 429 else 430 static if (is(T==enum)) 431 return readEnum!(T)(); 432 else 433 static if (is(T==string)) 434 return readString(); 435 else 436 static if (is(T==bool)) 437 return readBool(); 438 else 439 static if (is(T : real)) 440 return readNumber!(T)(); 441 else 442 static if (isDynamicArray!T) 443 return readArray!(typeof(T.init[0]))(); 444 else 445 static if (isStaticArray!T) 446 { 447 T result = readArray!(typeof(T.init[0]))()[]; 448 return result; 449 } 450 else 451 static if (isTuple!T) 452 return readTuple!T(); 453 else 454 static if (is(typeof(T.init.keys)) && is(typeof(T.init.values)) && is(typeof(T.init.keys[0])==string)) 455 return readAA!(T)(); 456 else 457 static if (is(T==JSONFragment)) 458 { 459 auto start = p; 460 skipValue(); 461 return JSONFragment(s[start..p]); 462 } 463 else 464 static if (is(T==struct)) 465 return readObject!(T)(); 466 else 467 static if (is(T U : U*)) 468 return readPointer!T(); 469 else 470 static assert(0, "Can't decode " ~ T.stringof ~ " from JSON"); 471 } 472 473 auto readTuple(T)() 474 { 475 // TODO: serialize as object if tuple has names 476 enum N = T.expand.length; 477 static if (N == 0) 478 return T(); 479 else 480 static if (N == 1) 481 return T(read!(typeof(T.expand[0]))); 482 else 483 { 484 T v; 485 expect('['); 486 foreach (n, ref f; v.expand) 487 { 488 static if (n) 489 expect(','); 490 f = read!(typeof(f)); 491 } 492 expect(']'); 493 return v; 494 } 495 } 496 497 auto readNullable(T)() 498 { 499 if (peek() == 'n') 500 { 501 next(); 502 expect('u'); 503 expect('l'); 504 expect('l'); 505 return Nullable!T(); 506 } 507 else 508 return Nullable!T(read!T); 509 } 510 511 C[] readSimpleString() /// i.e. without escapes 512 { 513 skipWhitespace(); 514 expect('"'); 515 auto start = p; 516 while (true) 517 { 518 auto c = next(); 519 if (c=='"') 520 break; 521 else 522 if (c=='\\') 523 throw new Exception("Unexpected escaped character"); 524 } 525 return s[start..p-1]; 526 } 527 528 string readString() 529 { 530 skipWhitespace(); 531 auto c = peek(); 532 if (c == '"') 533 { 534 next(); // '"' 535 string result; 536 auto start = p; 537 while (true) 538 { 539 c = next(); 540 if (c=='"') 541 break; 542 else 543 if (c=='\\') 544 { 545 result ~= s[start..p-1]; 546 switch (next()) 547 { 548 case '"': result ~= '"'; break; 549 case '/': result ~= '/'; break; 550 case '\\': result ~= '\\'; break; 551 case 'b': result ~= '\b'; break; 552 case 'f': result ~= '\f'; break; 553 case 'n': result ~= '\n'; break; 554 case 'r': result ~= '\r'; break; 555 case 't': result ~= '\t'; break; 556 case 'u': 557 { 558 wstring buf; 559 goto Unicode_start; 560 561 while (s[p..$].startsWith(`\u`)) 562 { 563 p+=2; 564 Unicode_start: 565 buf ~= cast(wchar)fromHex!ushort(readN(4)); 566 } 567 result ~= toUTF8(buf); 568 break; 569 } 570 default: enforce(false, "Unknown escape"); 571 } 572 start = p; 573 } 574 } 575 result ~= s[start..p-1]; 576 return result; 577 } 578 else 579 if (isDigit(c) || c=='-') // For languages that don't distinguish numeric strings from numbers 580 { 581 static immutable bool[256] numeric = 582 [ 583 '0':true, 584 '1':true, 585 '2':true, 586 '3':true, 587 '4':true, 588 '5':true, 589 '6':true, 590 '7':true, 591 '8':true, 592 '9':true, 593 '.':true, 594 '-':true, 595 '+':true, 596 'e':true, 597 'E':true, 598 ]; 599 600 auto start = p; 601 while (numeric[c = peek()]) 602 p++; 603 return s[start..p].idup; 604 } 605 else 606 { 607 foreach (n; "null") 608 expect(n); 609 return null; 610 } 611 } 612 613 bool readBool() 614 { 615 skipWhitespace(); 616 if (peek()=='t') 617 { 618 enforce(readN(4) == "true", "Bad boolean"); 619 return true; 620 } 621 else 622 if (peek()=='f') 623 { 624 enforce(readN(5) == "false", "Bad boolean"); 625 return false; 626 } 627 else 628 { 629 ubyte i = readNumber!ubyte(); 630 enforce(i < 2, "Bad digit for implicit number-to-bool conversion"); 631 return !!i; 632 } 633 } 634 635 T readNumber(T)() 636 { 637 skipWhitespace(); 638 T v; 639 const(char)[] n; 640 auto start = p; 641 char c = peek(); 642 if (c == '"') 643 n = readSimpleString(); 644 else 645 { 646 while (c=='+' || c=='-' || (c>='0' && c<='9') || c=='e' || c=='E' || c=='.') 647 { 648 p++; 649 if (eof) break; 650 c=peek(); 651 } 652 n = s[start..p]; 653 } 654 static if (is(T : real)) 655 return to!T(n); 656 else 657 static assert(0, "Don't know how to parse numerical type " ~ T.stringof); 658 } 659 660 T[] readArray(T)() 661 { 662 skipWhitespace(); 663 expect('['); 664 skipWhitespace(); 665 T[] result; 666 if (peek()==']') 667 { 668 p++; 669 return result; 670 } 671 while(true) 672 { 673 result ~= read!(T)(); 674 skipWhitespace(); 675 if (peek()==']') 676 { 677 p++; 678 return result; 679 } 680 else 681 expect(','); 682 } 683 } 684 685 T readObject(T)() 686 { 687 skipWhitespace(); 688 expect('{'); 689 skipWhitespace(); 690 T v; 691 if (peek()=='}') 692 { 693 p++; 694 return v; 695 } 696 697 while (true) 698 { 699 auto jsonField = readSimpleString(); 700 mixin(exceptionContext(q{"Error with field " ~ to!string(jsonField)})); 701 skipWhitespace(); 702 expect(':'); 703 704 bool found; 705 foreach (i, ref field; v.tupleof) 706 { 707 enum name = getJsonName!(T, v.tupleof[i].stringof[2..$]); 708 if (name == jsonField) 709 { 710 field = read!(typeof(v.tupleof[i]))(); 711 found = true; 712 break; 713 } 714 } 715 716 if (!found) 717 { 718 static if (hasAttribute!(JSONPartial, T)) 719 skipValue(); 720 else 721 throw new Exception(cast(string)("Unknown field " ~ jsonField)); 722 } 723 724 skipWhitespace(); 725 if (peek()=='}') 726 { 727 p++; 728 return v; 729 } 730 else 731 expect(','); 732 } 733 } 734 735 T readAA(T)() 736 { 737 skipWhitespace(); 738 static if (is(typeof(T.init is null))) 739 if (peek() == 'n') 740 { 741 next(); 742 expect('u'); 743 expect('l'); 744 expect('l'); 745 return null; 746 } 747 expect('{'); 748 skipWhitespace(); 749 T v; 750 if (peek()=='}') 751 { 752 p++; 753 return v; 754 } 755 756 while (true) 757 { 758 string jsonField = readString(); 759 skipWhitespace(); 760 expect(':'); 761 762 v[jsonField] = read!(typeof(v.values[0]))(); 763 764 skipWhitespace(); 765 if (peek()=='}') 766 { 767 p++; 768 return v; 769 } 770 else 771 expect(','); 772 } 773 } 774 775 T readEnum(T)() 776 { 777 return to!T(readSimpleString()); 778 } 779 780 T readPointer(T)() 781 { 782 skipWhitespace(); 783 if (peek()=='n') 784 { 785 enforce(readN(4) == "null", "Null expected"); 786 return null; 787 } 788 alias typeof(*T.init) S; 789 T v = new S; 790 *v = read!S(); 791 return v; 792 } 793 794 void skipValue() 795 { 796 skipWhitespace(); 797 char c = peek(); 798 switch (c) 799 { 800 case '"': 801 readString(); // TODO: Optimize 802 break; 803 case '0': .. case '9': 804 case '-': 805 readNumber!real(); // TODO: Optimize 806 break; 807 case '{': 808 next(); 809 bool first = true; 810 while (peek() != '}') 811 { 812 if (first) 813 first = false; 814 else 815 expect(','); 816 skipValue(); // key 817 expect(':'); 818 skipValue(); // value 819 } 820 expect('}'); 821 break; 822 case '[': 823 next(); 824 bool first = true; 825 while (peek() != ']') 826 { 827 if (first) 828 first = false; 829 else 830 expect(','); 831 skipValue(); 832 } 833 expect(']'); 834 break; 835 case 't': 836 foreach (l; "true") 837 expect(l); 838 break; 839 case 'f': 840 foreach (l; "false") 841 expect(l); 842 break; 843 case 'n': 844 foreach (l; "null") 845 expect(l); 846 break; 847 default: 848 throw new Exception("Can't parse: " ~ c); 849 } 850 } 851 } 852 853 T jsonParse(T, C)(C[] s) 854 { 855 auto parser = JsonParser!C(s); 856 mixin(exceptionContext(q{format("Error at position %d", parser.p)})); 857 return parser.read!T(); 858 } 859 860 unittest 861 { 862 struct S { int i; S[] arr; S* p0, p1; } 863 S s = S(42, [S(1), S(2)], null, new S(15)); 864 auto s2 = jsonParse!S(toJson(s)); 865 //assert(s == s2); // Issue 3789 866 assert(s.i == s2.i && s.arr == s2.arr && s.p0 is s2.p0 && *s.p1 == *s2.p1); 867 jsonParse!S(toJson(s).dup); 868 869 assert(jsonParse!(Tuple!())(``) == tuple()); 870 assert(jsonParse!(Tuple!int)(`42`) == tuple(42)); 871 assert(jsonParse!(Tuple!(int, string))(`[42, "banana"]`) == tuple(42, "banana")); 872 873 assert(jsonParse!(string[string])(`null`) is null); 874 } 875 876 // ************************************************************************ 877 878 // TODO: migrate to UDAs 879 880 /** 881 * A template that designates fields which should not be serialized to Json. 882 * 883 * Example: 884 * --- 885 * struct Point { int x, y, z; mixin NonSerialized!(x, z); } 886 * assert(jsonParse!Point(toJson(Point(1, 2, 3))) == Point(0, 2, 0)); 887 * --- 888 */ 889 template NonSerialized(fields...) 890 { 891 import ae.utils.meta : stringofArray; 892 mixin(NonSerializedFields(stringofArray!fields())); 893 } 894 895 string NonSerializedFields(string[] fields) 896 { 897 string result; 898 foreach (field; fields) 899 result ~= "enum bool " ~ field ~ "_nonSerialized = 1;"; 900 return result; 901 } 902 903 private template doSkipSerialize(T, string member) 904 { 905 enum bool doSkipSerialize = __traits(hasMember, T, member ~ "_nonSerialized"); 906 } 907 908 unittest 909 { 910 struct Point { int x, y, z; mixin NonSerialized!(x, z); } 911 assert(jsonParse!Point(toJson(Point(1, 2, 3))) == Point(0, 2, 0)); 912 } 913 914 unittest 915 { 916 enum En { one, two } 917 assert(En.one.toJson() == `"one"`); 918 struct S { int i1, i2; S[] arr1, arr2; string[string] dic; En en; mixin NonSerialized!(i2, arr2); } 919 S s = S(42, 5, [S(1), S(2)], [S(3), S(4)], ["apple":"fruit", "pizza":"vegetable"], En.two); 920 auto s2 = jsonParse!S(toJson(s)); 921 assert(s.i1 == s2.i1 && s2.i2 is int.init && s.arr1 == s2.arr1 && s2.arr2 is null && s.dic == s2.dic && s.en == En.two); 922 } 923 924 unittest 925 { 926 alias B = Nullable!bool; 927 B b; 928 929 b = jsonParse!B("true"); 930 assert(!b.isNull); 931 assert(b == true); 932 933 b = jsonParse!B("false"); 934 assert(!b.isNull); 935 assert(b == false); 936 937 b = jsonParse!B("null"); 938 assert(b.isNull); 939 } 940 941 unittest // Issue 49 942 { 943 immutable bool b; 944 assert(toJson(b) == "false"); 945 } 946 947 unittest 948 { 949 import ae.utils.aa; 950 alias M = OrderedMap!(string, int); 951 M m; 952 m["one"] = 1; 953 m["two"] = 2; 954 auto j = m.toJson(); 955 assert(j == `{"one":1,"two":2}`, j); 956 assert(j.jsonParse!M == m); 957 } 958 959 // ************************************************************************ 960 961 /// User-defined attribute - specify name for JSON object field. 962 /// Useful when a JSON object may contain fields, the name of which are not valid D identifiers. 963 struct JSONName { string name; } 964 965 private template getJsonName(S, string FIELD) 966 { 967 static if (hasAttribute!(JSONName, __traits(getMember, S, FIELD))) 968 enum getJsonName = getAttribute!(JSONName, __traits(getMember, S, FIELD)).name; 969 else 970 enum getJsonName = FIELD; 971 } 972 973 // ************************************************************************ 974 975 /// User-defined attribute - only serialize this field if its value is different from its .init value. 976 struct JSONOptional {} 977 978 unittest 979 { 980 static struct S { @JSONOptional bool a=true, b=false; } 981 assert(S().toJson == `{}`, S().toJson); 982 assert(S(false, true).toJson == `{"a":false,"b":true}`); 983 } 984 985 // ************************************************************************ 986 987 /// User-defined attribute - skip unknown fields when deserializing. 988 struct JSONPartial {} 989 990 unittest 991 { 992 @JSONPartial static struct S { int b; } 993 assert(`{"a":1,"b":2,"c":3.4,"d":[5,"x"],"de":[],"e":{"k":"v"},"ee":{},"f":true,"g":false,"h":null}`.jsonParse!S == S(2)); 994 } 995 996 // ************************************************************************ 997 998 /// Fragment of raw JSON. 999 /// When serialized, the .json field is inserted into the resulting 1000 /// string verbatim, without any validation. 1001 /// When deserialized, will contain the raw JSON of one JSON object of 1002 /// any type. 1003 struct JSONFragment { string json; } 1004 1005 unittest 1006 { 1007 import std.conv; 1008 JSONFragment[] arr = [JSONFragment(`1`), JSONFragment(`true`), JSONFragment(`"foo"`), JSONFragment(`[55]`)]; 1009 assert(arr.toJson == `[1,true,"foo",[55]]`); 1010 assert(arr.toJson.jsonParse!(JSONFragment[]) == arr); 1011 }