1 /******************************************************************************* 2 3 Copyright: Copyright (C) 2008 Aaron Craelius & Kris Bell 4 All rights reserved 5 6 License: BSD style: $(LICENSE) 7 8 version: July 2008: Initial release 9 10 Authors: Aaron, Kris 11 12 *******************************************************************************/ 13 14 module tango.text.json.Json; 15 16 private import tango.core.Vararg; 17 18 private import tango.io.model.IConduit; 19 20 private import tango.text.json.JsonEscape; 21 22 private import tango.text.json.JsonParser; 23 24 private import Float = tango.text.convert.Float; 25 26 private import Utf = tango.text.convert.Utf; 27 28 /******************************************************************************* 29 30 Parse json text into a set of inter-related structures. Typical 31 usage is as follows: 32 --- 33 auto json = new Json!(char); 34 json.parse (`{"t": true, "n":null, "array":["world", [4, 5]]}`); 35 --- 36 37 Converting back to text format employs a delegate. This one emits 38 document content to the console: 39 --- 40 json.print ((char[] s) {Stdout(s);}); 41 --- 42 43 Constructing json within your code leverages a handful of factories 44 within a document instance. This example creates a document from an 45 array of values: 46 --- 47 auto json = new Json!(char); 48 49 // [true, false, null, "text"] 50 with (json) 51 value = array (true, false, null, "text"); 52 --- 53 54 Setting the document to contain a simple object instead: 55 --- 56 // {"a" : 10} 57 with (json) 58 value = object (pair("a", value(10))); 59 --- 60 61 Objects may be constructed with multiple attribute pairs like so: 62 --- 63 // {"a" : 10, "b" : true} 64 with (json) 65 value = object (pair("a", value(10)), pair("b", value(true))); 66 --- 67 68 Substitute arrays, or other objects as values where appropriate: 69 --- 70 // {"a" : [10, true, {"b" : null}]} 71 with (json) 72 value = object (pair("a", array(10, true, object(pair("b"))))); 73 --- 74 75 TODO: document how to extract content 76 77 Big thanks to dhasenan for suggesting the construction notation. We 78 can't make effective use of operator-overloading, due to the use of 79 struct pointers, so this syntax turned out to be the next best thing. 80 81 *******************************************************************************/ 82 83 class Json(T) : JsonParser!(T) 84 { 85 /// use these types for external references 86 public alias JsonValue* Value; 87 public alias NameValue* Attribute; 88 public alias JsonObject* Composite; 89 90 /// enumerates the seven acceptable JSON value types 91 public enum Type {Null, String, RawString, Number, Object, Array, True, False}; 92 93 private Value root; 94 95 /*********************************************************************** 96 97 Construct a json instance, with a default value of null 98 99 ***********************************************************************/ 100 101 this () 102 { 103 arrays.length = 16; 104 parse (null); 105 } 106 107 /*********************************************************************** 108 109 Parse the given text and return a resultant Value type. Also 110 sets the document value. 111 112 ***********************************************************************/ 113 114 final Value parse (const(T)[] json) 115 { 116 nesting = 0; 117 attrib.reset(); 118 values.reset(); 119 objects.reset(); 120 foreach (ref p; arrays) 121 p.index = 0; 122 123 root = createValue(); 124 if (super.reset (json)) 125 { 126 if (curType is Token.BeginObject) 127 root.set (parseObject()); 128 else 129 if (curType is Token.BeginArray) 130 root.set (parseArray()); 131 else 132 exception ("invalid json document"); 133 } 134 return root; 135 } 136 137 /*********************************************************************** 138 139 Return a text representation of this document 140 141 ***********************************************************************/ 142 143 final T[] toString (const(T)[] space, int decimals=2) 144 { 145 return root.print (space, decimals); 146 } 147 148 final override 149 immutable(char)[] toString() 150 { 151 return Utf.toString(toString(null)).idup; 152 } 153 154 /*********************************************************************** 155 156 Returns the root value of this document 157 158 ***********************************************************************/ 159 160 final Value value () 161 { 162 return root; 163 } 164 165 /*********************************************************************** 166 167 Set the root value of this document 168 169 ***********************************************************************/ 170 171 final Value value (Value v) 172 { 173 return root = v; 174 } 175 176 /*********************************************************************** 177 178 Create a text value 179 180 ***********************************************************************/ 181 182 final Value value (const(T)[] v) 183 { 184 return createValue().set (v); 185 } 186 187 /*********************************************************************** 188 189 Create a boolean value 190 191 ***********************************************************************/ 192 193 final Value value (bool v) 194 { 195 return createValue().set (v); 196 } 197 198 /*********************************************************************** 199 200 Create a numeric value 201 202 ***********************************************************************/ 203 204 final Value value (double v) 205 { 206 return createValue().set (v); 207 } 208 209 /*********************************************************************** 210 211 Create a single Value from an array of Values 212 213 ***********************************************************************/ 214 215 final Value value (Value[] vals) 216 { 217 return createValue().set (vals); 218 } 219 220 /*********************************************************************** 221 222 Create an array of values 223 224 ***********************************************************************/ 225 226 final Value array (...) 227 { 228 return createValue().set (this, _arguments, _argptr); 229 } 230 231 /*********************************************************************** 232 233 Create an attribute/value pair, where value defaults to 234 null 235 236 ***********************************************************************/ 237 238 Attribute pair (const(T)[] name, Value value = null) 239 { 240 if (value is null) 241 value = createValue(); 242 return createAttribute().set (name, value); 243 } 244 245 /*********************************************************************** 246 247 Create a composite from zero or more pairs, and return as 248 a value 249 250 ***********************************************************************/ 251 252 final Value object (Attribute[] set...) 253 { 254 return createValue().set (createObject().add (set)); 255 } 256 257 /*********************************************************************** 258 259 Internal factory to create values 260 261 ***********************************************************************/ 262 263 private Value createValue () 264 { 265 return values.allocate().reset(); 266 } 267 268 /*********************************************************************** 269 270 Internal factory to create composites 271 272 ***********************************************************************/ 273 274 private Composite createObject () 275 { 276 return objects.allocate().reset(); 277 } 278 279 /*********************************************************************** 280 281 Internal factory to create attributes 282 283 ***********************************************************************/ 284 285 private Attribute createAttribute () 286 { 287 return attrib.allocate(); 288 } 289 290 /*********************************************************************** 291 292 Throw a generic exception 293 294 ***********************************************************************/ 295 296 private void exception (immutable(char)[] msg) 297 { 298 throw new Exception (msg); 299 } 300 301 /*********************************************************************** 302 303 Parse an instance of a value 304 305 ***********************************************************************/ 306 307 private Value parseValue () 308 { 309 auto v = values.allocate(); 310 311 switch (super.curType) 312 { 313 case Token.True: 314 v.set (Type.True); 315 break; 316 317 case Token.False: 318 v.set (Type.False); 319 break; 320 321 case Token.Null: 322 v.set (Type.Null); 323 break; 324 325 case Token.BeginObject: 326 v.set (parseObject()); 327 break; 328 329 case Token.BeginArray: 330 v.set (parseArray()); 331 break; 332 333 case Token.String: 334 v.set (super.value, true); 335 break; 336 337 case Token.Number: 338 v.set (Float.parse (super.value)); 339 break; 340 341 default: 342 v.set (Type.Null); 343 break; 344 } 345 346 return v; 347 } 348 349 /*********************************************************************** 350 351 Parse an object declaration 352 353 ***********************************************************************/ 354 355 private Composite parseObject () 356 { 357 auto o = objects.allocate().reset(); 358 359 while (super.next) 360 { 361 if (super.curType is Token.EndObject) 362 return o; 363 364 if (super.curType != Token.Name) 365 super.expected ("an attribute-name", super.str.ptr); 366 367 auto name = super.value; 368 369 if (! super.next) 370 super.expected ("an attribute-value", super.str.ptr); 371 372 o.append (attrib.allocate().set (name, parseValue())); 373 } 374 375 return o; 376 } 377 378 /*********************************************************************** 379 380 Parse an array declaration 381 382 ***********************************************************************/ 383 384 private Value[] parseArray () 385 { 386 if (nesting >= arrays.length) 387 exception ("array nesting too deep within document"); 388 389 auto array = &arrays[nesting++]; 390 auto start = array.index; 391 392 while (super.next && super.curType != Token.EndArray) 393 { 394 if (array.index >= array.content.length) 395 array.content.length = array.content.length + 300; 396 397 array.content [array.index++] = parseValue(); 398 } 399 400 if (super.curType != Token.EndArray) 401 exception ("malformed array"); 402 403 --nesting; 404 return array.content [start .. array.index]; 405 } 406 407 /*********************************************************************** 408 409 Represents an attribute/value pair. Aliased as Attribute 410 411 ***********************************************************************/ 412 413 struct NameValue 414 { 415 private Attribute next; 416 public const(T)[] name; 417 public Value value; 418 419 /*************************************************************** 420 421 Set a name and a value for this attribute 422 423 Returns itself, for use with Composite.add() 424 425 ***************************************************************/ 426 427 Attribute set (const(T)[] key, Value val) 428 { 429 name = key; 430 value = val; 431 return &this; 432 } 433 } 434 435 /*********************************************************************** 436 437 Represents a single json Object (a composite of named 438 attribute/value pairs). 439 440 This is aliased as Composite 441 442 ***********************************************************************/ 443 444 struct JsonObject 445 { 446 private Attribute head, 447 tail; 448 449 /*************************************************************** 450 451 ***************************************************************/ 452 453 Composite reset () 454 { 455 head = tail = null; 456 return &this; 457 } 458 459 /*************************************************************** 460 461 Append an attribute/value pair 462 463 ***************************************************************/ 464 465 Composite append (Attribute a) 466 { 467 if (tail) 468 tail.next = a, tail = a; 469 else 470 head = tail = a; 471 return &this; 472 } 473 474 /*************************************************************** 475 476 Add a set of attribute/value pairs 477 478 ***************************************************************/ 479 480 Composite add (Attribute[] set...) 481 { 482 foreach (attr; set) 483 append (attr); 484 return &this; 485 } 486 487 /*************************************************************** 488 489 Construct and return a hashmap of Object attributes. 490 This will be a fairly costly operation, so consider 491 alternatives where appropriate 492 493 ***************************************************************/ 494 495 Value[T[]] hashmap () 496 { 497 Value[T[]] members; 498 499 auto a = head; 500 while (a) 501 { 502 members[a.name] = a.value; 503 a = a.next; 504 } 505 506 return members; 507 } 508 509 /*************************************************************** 510 511 Return a corresponding value for the given attribute 512 name. Does a linear lookup across the attribute set 513 514 ***************************************************************/ 515 516 Value value (const(T)[] name) 517 { 518 auto a = head; 519 while (a) 520 if (name == a.name) 521 return a.value; 522 else 523 a = a.next; 524 525 return null; 526 } 527 528 /*************************************************************** 529 530 Iterate over our attribute names and values 531 532 ***************************************************************/ 533 534 @property Iterator attributes () 535 { 536 Iterator i = {head}; 537 return i; 538 } 539 540 /*************************************************************** 541 542 Iterate over our attribute names. Note that we 543 use a Fruct to handle this, since foreach does 544 not operate cleanly with pointers (it doesn't 545 automatically dereference them), whereas using 546 x.attributes() does. 547 548 We may also use this to do some name filtering 549 550 ***************************************************************/ 551 552 static struct Iterator 553 { 554 private Attribute head; 555 556 int opApply (int delegate(ref const(T)[] key, ref Value val) dg) 557 { 558 int res; 559 560 auto a = head; 561 while (a) 562 { 563 if ((res = dg (a.name, a.value)) != 0) 564 break; 565 a = a.next; 566 } 567 return res; 568 } 569 } 570 } 571 572 /*********************************************************************** 573 574 Represents a json value that is one of the seven types 575 specified via the Json.Type enum 576 577 ***********************************************************************/ 578 579 struct JsonValue 580 { 581 private union 582 { 583 Value[] array; 584 real number; 585 const(T)[] string; 586 Composite object; 587 } 588 589 public Type type; /// the type of this node 590 alias reset set; /// alternate name for reset 591 592 /*************************************************************** 593 594 return true if this node is of the given type 595 596 ***************************************************************/ 597 598 bool equals (Type t) 599 { 600 return type is t; 601 } 602 603 /*************************************************************** 604 605 Return true if this value represent True 606 607 ***************************************************************/ 608 609 bool toBool () 610 { 611 return (type is Type.True); 612 } 613 614 /*************************************************************** 615 616 Return the string content. Returns null if this 617 value is not a string. 618 619 Uses dst for escape conversion where possible. 620 621 ***************************************************************/ 622 623 const(T)[] toString (T[] dst) 624 { 625 if (type is Type.RawString) 626 return string; 627 628 if (type is Type.String) 629 return unescape (string, dst); 630 631 return null; 632 } 633 634 immutable(char)[] toString() 635 { 636 return Utf.toString(toString(cast(T[])null)).idup; 637 } 638 639 /*************************************************************** 640 641 Emit the string content to the given delegate, with 642 escape conversion as required. 643 644 Returns false if this is not a String value 645 646 ***************************************************************/ 647 648 bool toString (scope void delegate(const(T)[]) dg) 649 { 650 if (type is Type.RawString) 651 dg(string); 652 else 653 if (type is Type.String) 654 unescape (string, dg); 655 else 656 return false; 657 return true; 658 } 659 660 /*************************************************************** 661 662 Return the content as a Composite/Object. Returns null 663 if this value is not a Composite. 664 665 ***************************************************************/ 666 667 Composite toObject () 668 { 669 return type is Type.Object ? object : null; 670 } 671 672 /*************************************************************** 673 674 Return the content as a double. Returns nan where 675 the value is not numeric. 676 677 ***************************************************************/ 678 679 real toNumber () 680 { 681 return type is Type.Number ? number : real.nan; 682 } 683 684 /*************************************************************** 685 686 Return the content as an array. Returns null where 687 the value is not an array. 688 689 ***************************************************************/ 690 691 Value[] toArray () 692 { 693 return (type is Type.Array) ? array : null; 694 } 695 696 /*************************************************************** 697 698 Set this value to represent a string. If 'escaped' 699 is set, the string is assumed to have pre-converted 700 escaping of reserved characters (such as \t). 701 702 ***************************************************************/ 703 704 Value set (const(T)[] str, bool escaped = false) 705 { 706 type = escaped ? Type.String : Type.RawString; 707 string = str; 708 return &this; 709 } 710 711 /*************************************************************** 712 713 Set this value to represent an object. 714 715 ***************************************************************/ 716 717 Value set (Composite obj) 718 { 719 type = Type.Object; 720 object = obj; 721 return &this; 722 } 723 724 /*************************************************************** 725 726 Set this value to represent a number. 727 728 ***************************************************************/ 729 730 Value set (real num) 731 { 732 type = Type.Number; 733 number = num; 734 return &this; 735 } 736 737 /*************************************************************** 738 739 Set this value to represent a boolean. 740 741 ***************************************************************/ 742 743 Value set (bool b) 744 { 745 type = b ? Type.True : Type.False; 746 return &this; 747 } 748 749 /*************************************************************** 750 751 Set this value to represent an array of values. 752 753 ***************************************************************/ 754 755 Value set (Value[] a) 756 { 757 type = Type.Array; 758 array = a; 759 return &this; 760 } 761 762 /*************************************************************** 763 764 Set this value to represent null 765 766 ***************************************************************/ 767 768 Value reset () 769 { 770 type = Type.Null; 771 return &this; 772 } 773 774 /*************************************************************** 775 776 Return a text representation of this value 777 778 ***************************************************************/ 779 780 T[] print (const(T)[] space=null, int decimals=2) 781 { 782 T[] tmp; 783 void append (const(T)[] s) {tmp ~= s;} 784 print (&append, space, decimals); 785 return tmp; 786 } 787 788 /*************************************************************** 789 790 Emit a text representation of this value to the 791 given OutputStream 792 793 ***************************************************************/ 794 795 Value print (OutputStream s, const(T)[] space=null, int decimals=2) 796 { 797 return print ((const(T)[] t){s.write(t);}, space, decimals); 798 } 799 800 /*************************************************************** 801 802 Emit a text representation of this value to the 803 provided delegate 804 805 ***************************************************************/ 806 807 Value print (void delegate(const(T)[]) append, const(T)[] space=null, int decimals=2) 808 { 809 auto indent = 0; 810 811 @property void newline () 812 { 813 if (space.length) 814 { 815 append ("\n"); 816 for (auto i=0; i < indent; i++) 817 append (space); 818 } 819 } 820 821 void printValue (Value val) 822 { 823 void printObject (Composite obj) 824 { 825 if (obj is null) 826 return; 827 828 bool first = true; 829 append ("{"); 830 indent++; 831 832 foreach (k, v; obj.attributes) 833 { 834 if (!first) 835 append (","); 836 newline; 837 append (`"`), append(k), append(`":`); 838 printValue (v); 839 first = false; 840 } 841 indent--; 842 newline; 843 append ("}"); 844 } 845 846 void printArray (Value[] arr) 847 { 848 bool first = true; 849 append ("["); 850 indent++; 851 foreach (v; arr) 852 { 853 if (!first) 854 append (", "); 855 newline; 856 printValue (v); 857 first = false; 858 } 859 indent--; 860 newline; 861 append ("]"); 862 } 863 864 865 if (val is null) 866 return; 867 868 switch (val.type) 869 { 870 T[64] tmp = void; 871 872 case Type.String: 873 append (`"`), append(val..string), append(`"`); 874 break; 875 876 case Type.RawString: 877 append (`"`), escape(val..string, append), append(`"`); 878 break; 879 880 case Type.Number: 881 append (Float.format (tmp, val.toNumber(), decimals)); 882 break; 883 884 case Type.Object: 885 auto obj = val.toObject(); 886 debug assert(obj !is null); 887 printObject (val.toObject()); 888 break; 889 890 case Type.Array: 891 printArray (val.toArray()); 892 break; 893 894 case Type.True: 895 append ("true"); 896 break; 897 898 case Type.False: 899 append ("false"); 900 break; 901 902 default: 903 case Type.Null: 904 append ("null"); 905 break; 906 } 907 } 908 909 printValue (&this); 910 return &this; 911 } 912 913 /*************************************************************** 914 915 Set to a specified type 916 917 ***************************************************************/ 918 919 private Value set (Type type) 920 { 921 this.type = type; 922 return &this; 923 } 924 925 /*************************************************************** 926 927 Set a variety of values into an array type 928 929 ***************************************************************/ 930 931 private Value set (Json host, TypeInfo[] info, va_list args) 932 { 933 Value[] list; 934 935 foreach (type; info) 936 { 937 Value v; 938 if (type is typeid(Value)) 939 v = va_arg!(Value)(args); 940 else 941 { 942 v = host.createValue(); 943 if (type is typeid(double)) 944 v.set (va_arg!(double)(args)); 945 else 946 if (type is typeid(int)) 947 v.set (va_arg!(int)(args)); 948 else 949 if (type is typeid(bool)) 950 v.set (va_arg!(bool)(args)); 951 else 952 if (type is typeid(long)) 953 v.set (va_arg!(long)(args)); 954 else 955 if (type is typeid(Composite)) 956 v.set (va_arg!(Composite)(args)); 957 else 958 if (type is typeid(T[])) 959 v.set (va_arg!(T[])(args)); 960 else 961 if (type is typeid(void*)) 962 va_arg!(void*)(args); 963 else 964 host.exception ("JsonValue.set :: unexpected type: "~type.toString()); 965 } 966 list ~= v; 967 } 968 /* For some reason DMD 2.054 doesn't like calling set here directly */ 969 return forwardref_buf (&this, list); 970 } 971 972 private static Value forwardref_buf(Value v, Value[] list) 973 { 974 return v.set(list); 975 } 976 } 977 978 /*********************************************************************** 979 980 Internal allocation mechanism 981 982 ***********************************************************************/ 983 984 private struct Allocator(T) 985 { 986 private T[] list; 987 private T[][] lists; 988 private int index, 989 block; 990 991 void reset () 992 { 993 // discard since prior lists are not initialized 994 lists.length = 0; 995 block = -1; 996 newlist; 997 } 998 999 T* allocate () 1000 { 1001 if (index >= list.length) 1002 newlist; 1003 1004 auto p = &list [index++]; 1005 return p; 1006 } 1007 1008 @property private void newlist () 1009 { 1010 index = 0; 1011 if (++block >= lists.length) 1012 { 1013 lists.length = lists.length + 1; 1014 lists[$-1] = new T[256]; 1015 } 1016 list = lists [block]; 1017 } 1018 } 1019 1020 /*********************************************************************** 1021 1022 Internal use for parsing array values 1023 1024 ***********************************************************************/ 1025 1026 private struct Array 1027 { 1028 uint index; 1029 Value[] content; 1030 } 1031 1032 /*********************************************************************** 1033 1034 Internal document representation 1035 1036 ***********************************************************************/ 1037 1038 private alias Allocator!(NameValue) Attrib; 1039 private alias Allocator!(JsonValue) Values; 1040 private alias Allocator!(JsonObject) Objects; 1041 1042 private Attrib attrib; 1043 private Values values; 1044 private Array[] arrays; 1045 private Objects objects; 1046 private uint nesting; 1047 } 1048 1049 1050 1051 /******************************************************************************* 1052 1053 *******************************************************************************/ 1054 1055 debug (UnitTest) 1056 { 1057 unittest 1058 { 1059 with (new Json!(char)) 1060 { 1061 root = object 1062 ( 1063 pair ("edgar", value("friendly")), 1064 pair ("count", value(11.5)), 1065 pair ("array", value(array(1, 2))) 1066 ); 1067 1068 auto value = toString(); 1069 assert (value == `{"edgar":"friendly","count":11.5,"array":[1, 2]}`, value); 1070 } 1071 } 1072 1073 unittest 1074 { 1075 // check with a separator of the tab character 1076 with (new Json!(char)) 1077 { 1078 root = object 1079 ( 1080 pair ("edgar", value("friendly")), 1081 pair ("count", value(11.5)), 1082 pair ("array", value(array(1, 2))) 1083 ); 1084 1085 auto value = toString ("\t"); 1086 assert (value == "{\n\t\"edgar\":\"friendly\",\n\t\"count\":11.5,\n\t\"array\":[\n\t\t1, \n\t\t2\n\t]\n}", value); 1087 } 1088 } 1089 1090 unittest 1091 { 1092 // check with a separator of five spaces 1093 with (new Json!(dchar)) 1094 { 1095 root = object 1096 ( 1097 pair ("edgar", value("friendly")), 1098 pair ("count", value(11.5)), 1099 pair ("array", value(array(1, 2))) 1100 ); 1101 1102 auto value = toString (" "); 1103 assert (value == "{\n \"edgar\":\"friendly\",\n \"count\":11.5,\n \"array\":[\n 1, \n 2\n ]\n}"); 1104 } 1105 } 1106 } 1107 1108 /******************************************************************************* 1109 1110 *******************************************************************************/ 1111 1112 debug (Json) 1113 { 1114 import tango.io.Stdout; 1115 import tango.io.device.File; 1116 import tango.time.StopWatch; 1117 1118 void main() 1119 { 1120 void loop (JsonParser!(char) parser, char[] json, int n) 1121 { 1122 for (uint i = 0; i < n; ++i) 1123 { 1124 parser.reset (json); 1125 while (parser.next) {} 1126 } 1127 } 1128 1129 void test (char[] filename, char[] txt) 1130 { 1131 uint n = (300 * 1024 * 1024) / txt.length; 1132 auto parser = new JsonParser!(char); 1133 1134 StopWatch watch; 1135 watch.start; 1136 loop (parser, txt, n); 1137 auto t = watch.stop; 1138 auto mb = (txt.length * n) / (1024 * 1024); 1139 Stdout.formatln("{} {} iterations, {} seconds: {} MB/s", filename, n, t, mb/t); 1140 } 1141 1142 void test1 (char[] filename, char[] txt) 1143 { 1144 uint n = (200 * 1024 * 1024) / txt.length; 1145 auto parser = new Json!(char); 1146 1147 StopWatch watch; 1148 watch.start; 1149 for (uint i = 0; i < n; ++i) 1150 parser.parse (txt); 1151 1152 auto t = watch.stop; 1153 auto mb = (txt.length * n) / (1024 * 1024); 1154 Stdout.formatln("{} {} iterations, {} seconds: {} MB/s", filename, n, t, mb/t); 1155 } 1156 1157 char[] load (char[] file) 1158 { 1159 return cast(char[]) File.get(file); 1160 } 1161 1162 //test("test1.json", load("test1.json")); 1163 //test("test2.json", load("test2.json")); 1164 //test("test3.json", load("test3.json")); 1165 1166 //test1("test1.json", load("test1.json")); 1167 //test1("test2.json", load("test2.json")); 1168 //test1("test3.json", load("test3.json")); 1169 1170 auto p = new Json!(char); 1171 auto v = p.parse (`{"t": true, "f":false, "n":null, "hi":["world", "big", 123, [4, 5, ["foo"]]]}`); 1172 Stdout.formatln ("{}", p.toString()); 1173 1174 with (p) 1175 value = object(pair("a", array(null, true, false, 30, object(pair("foo")))), pair("b", value(10))); 1176 1177 Stdout.formatln ("{}", p.toString()); 1178 1179 p.parse ("[-1]"); 1180 Stdout.formatln ("{}", p.toString(null)); 1181 1182 p.parse ("[11.23477]"); 1183 Stdout.formatln ("{}", p.toString(null, 4)); 1184 1185 p.parse(`["foo"]`); 1186 Stdout.formatln ("{}", p.toString()); 1187 1188 p.parse(`{"foo": {"ff" : "ffff"}`); 1189 Stdout.formatln ("{}", p.toString()); 1190 1191 with (new Json!(char)) 1192 { 1193 root = object(pair("array", array(null))); 1194 Stdout.formatln ("{}", toString()); 1195 } 1196 } 1197 } 1198 1199 1200