1 module dutils.data.json;
2 
3 void populateFromJSON(T)(ref T object, ref JSON data) {
4   populateFromJSON(object, data, "");
5 }
6 
7 void populateFromJSON(T)(ref T object, ref JSON data, string pathPrefix = "") {
8   import std.conv : to;
9   import std.datetime.systime : SysTime;
10   import std.uuid : UUID;
11   import std.traits : isSomeFunction, isNumeric, isSomeString, isBoolean; //, isArray, isBuiltinType;
12   import dutils.validation.validate : validate, ValidationError, ValidationErrors;
13   import dutils.validation.constraints : ValidationErrorTypes;
14 
15   ValidationError[] errors;
16 
17   static foreach (member; __traits(derivedMembers, T)) {
18     static if (!isSomeFunction!(__traits(getMember, T, member))) {
19       if (data[member].type != JSON.Type.undefined) {
20         auto JSONValue = data[member];
21         auto JSONType = JSONValue.type;
22         const path = pathPrefix == "" ? member : pathPrefix ~ "." ~ member;
23 
24         try {
25           alias structType = typeof(__traits(getMember, T, member));
26 
27           if (isNumeric!(structType) && (JSONType == JSON.Type.int_
28               || JSONType == JSON.Type.bigInt || JSONType == JSON.Type.float_)) {
29             __traits(getMember, object, member) = JSONValue.to!(structType);
30           } else if ((isSomeString!(structType) || is(structType == UUID)
31               || is(structType == SysTime)) && JSONType == JSON.Type..string) {
32             __traits(getMember, object, member) = JSONValue.to!(structType);
33           } else if (isBoolean!(structType)) {
34             __traits(getMember, object, member) = JSONValue.to!(structType);
35           }  /*
36           // TODO: support array recursion
37           } else if (isArray!(structType)) {
38               foreach (child; JSONValue) {
39                   __traits(getMember, object, member) ~=
40               }
41           // TODO: support nested calls
42           } else if (!isBuiltinType!(structType)) {
43               fromJSON( __traits(getMember, object, member), JSONValue, path);
44           }
45           */
46           else {
47             throw new Exception("Unsupported type");
48           }
49         } catch (Exception error) {
50           errors ~= ValidationError(path, ValidationErrorTypes.type,
51               "Value must be convertable to type " ~ typeof(__traits(getMember,
52                 T, member)).stringof ~ " but got " ~ data[member].type.to!string);
53         }
54       }
55     }
56   }
57 
58   try {
59     validate(object);
60   } catch (ValidationErrors validation) {
61     errors ~= validation.errors;
62   }
63 
64   if (errors.length > 0) {
65     throw new ValidationErrors(errors);
66   }
67 }
68 
69 /**
70  * populateFromJSON - ensure that deserialization works with valid JSON data
71  */
72 unittest {
73   import dutils.validation.constraints : ValidateMinimumLength,
74     ValidateMaximumLength, ValidateMinimum, ValidateEmail, ValidateRequired;
75 
76   struct Person {
77     @ValidateMinimumLength(2)
78     @ValidateMaximumLength(100)
79     string name;
80 
81     @ValidateMinimum!double(20) double height;
82 
83     @ValidateEmail()
84     @ValidateRequired()
85     string email;
86 
87     @ValidateRequired()
88     bool member;
89   }
90 
91   auto data = JSON([
92       "does not exists": JSON(true),
93       "name": JSON("Anna"),
94       "height": JSON(170.1),
95       "email": JSON("anna@example.com"),
96       "member": JSON(true)
97       ]);
98 
99   Person person;
100   populateFromJSON(person, data);
101 
102   assert(person.name == "Anna", "expected name Anna");
103   assert(person.height == 170.1, "expected height 170.1");
104   assert(person.email == "anna@example.com", "expected email anna@example.com");
105   assert(person.member == true, "expected member true");
106 }
107 
108 /**
109  * populateFromJSON - ensure that validation errors are thrown with invalid JSON data
110  */
111 unittest {
112   import std.conv : to;
113   import dutils.validation.validate : ValidationError, ValidationErrors;
114   import dutils.validation.constraints : ValidationErrorTypes, ValidateMinimumLength,
115     ValidateMaximumLength, ValidateMinimum, ValidateEmail, ValidateRequired;
116 
117   struct Person {
118     @ValidateMinimumLength(2)
119     @ValidateMaximumLength(100)
120     string name;
121 
122     @ValidateMinimum!float(20) float height;
123 
124     @ValidateEmail()
125     @ValidateRequired()
126     string email;
127 
128     @ValidateRequired()
129     bool member;
130   }
131 
132   auto data = JSON([
133       "does not exists": JSON(true),
134       "name": JSON("Anna"),
135       "height": JSON("not a number")
136       ]);
137 
138   Person person;
139 
140   auto catched = false;
141   try {
142     populateFromJSON(person, data);
143   } catch (ValidationErrors validation) {
144     catched = true;
145     assert(validation.errors.length == 4,
146         "expected 4 errors, got " ~ validation.errors.length.to!string
147         ~ " with message: " ~ validation.msg);
148     assert(validation.errors[0].type == "type", "expected minimumLength error");
149     assert(validation.errors[0].path == "height", "expected error path to be height");
150     assert(validation.errors[1].type == "minimum", "expected minimum error");
151     assert(validation.errors[1].path == "height", "expected error path to be height");
152     assert(validation.errors[2].type == "required", "expected required error");
153     assert(validation.errors[2].path == "email", "expected error path to be email");
154     assert(validation.errors[3].type == "required", "expected required error");
155     assert(validation.errors[3].path == "member", "expected error path to be member");
156   }
157 
158   assert(catched == true, "did not catch the expected errors");
159 }
160 
161 /**
162  * populateFromJSON - should populate
163  */
164 unittest {
165   import dutils.validation.constraints : ValidateRequired, ValidateEmail;
166 
167   struct Email {
168     @ValidateRequired()
169     @ValidateEmail()
170     string to;
171 
172     @ValidateEmail()
173     string from;
174 
175     string subject;
176 
177     @ValidateRequired()
178     string body;
179   }
180 
181   auto data = JSON([
182       "does not exists": JSON(true),
183       "to": JSON("anna@example.com"),
184       "body": JSON("Some text")
185       ]);
186 
187   Email email;
188   populateFromJSON(email, data);
189 
190   assert(email.to == "anna@example.com", "expected to to be anna@example.com");
191   assert(email.from == "", "expected from to be \"\"");
192   assert(email.subject == "", "expected from to be \"\"");
193   assert(email..body == "Some text", "expected from to be \"Some text\"");
194 }
195 
196 /**
197 	JSON serialization and value handling.
198 
199 	This module provides the JSON struct for reading, writing and manipulating
200 	JSON values. De(serialization) of arbitrary D types is also supported and
201 	is recommended for handling JSON in performance sensitive applications.
202 
203 	Copyright: © 2012-2015 Sönke Ludwig
204 	License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file.
205 	Authors: Sönke Ludwig
206 */
207 
208 import dutils.data.utils.serialization;
209 
210 ///
211 @safe unittest {
212   void manipulateJSON(JSON j) {
213     import std.stdio;
214 
215     // retrieving the values is done using get()
216     assert(j["name"].get!string == "Example");
217     assert(j["id"].get!int == 1);
218 
219     // semantic conversions can be done using to()
220     assert(j["id"].to!string == "1");
221 
222     // prints:
223     // name: "Example"
224     // id: 1
225     foreach (key, value; j.byKeyValue)
226       writefln("%s: %s", key, value);
227 
228     // print out as JSON: {"name": "Example", "id": 1}
229     writefln("JSON: %s", j.toString());
230   }
231 }
232 
233 /// Constructing `JSON` objects
234 @safe unittest {
235   // construct a JSON object {"field1": "foo", "field2": 42, "field3": true}
236 
237   // using the constructor
238   JSON j1 = JSON([
239       "field1": JSON("foo"),
240       "field2": JSON(42),
241       "field3": JSON(true)
242       ]);
243 
244   // using piecewise construction
245   JSON j2 = JSON.emptyObject;
246   j2["field1"] = "foo";
247   j2["field2"] = 42.0;
248   j2["field3"] = true;
249 
250   // using serialization
251   struct S {
252     string field1;
253     double field2;
254     bool field3;
255   }
256 
257   JSON j3 = S("foo", 42, true).serializeToJSON();
258 
259   // using serialization, converting directly to a JSON string
260   string j4 = S("foo", 32, true).serializeToJSONString();
261 }
262 
263 public import std.json : JSONException;
264 import std.algorithm;
265 import std.array;
266 import std.bigint;
267 import std.conv;
268 import std.datetime;
269 import std.exception;
270 import std.format;
271 
272 static if (__VERSION__ >= 2082) {
273   import std.json : JSONValue, JSONType;
274 } else {
275   import std.json : JSONValue, JSON_TYPE;
276 
277   private enum JSONType : byte {
278     null_ = JSON_TYPE.NULL,
279     string = JSON_TYPE.STRING,
280     integer = JSON_TYPE.INTEGER,
281     uinteger = JSON_TYPE.UINTEGER,
282     float_ = JSON_TYPE.FLOAT,
283     array = JSON_TYPE.ARRAY,
284     object = JSON_TYPE.OBJECT,
285     true_ = JSON_TYPE.TRUE,
286     false_ = JSON_TYPE.FALSE,
287   }
288 }
289 import std.range;
290 import std.string;
291 import std.traits;
292 import std.typecons : Tuple;
293 import std.uuid;
294 
295 /******************************************************************************/
296 /* public types                                                               */
297 /******************************************************************************/
298 
299 /**
300 	Represents a single JSON value.
301 
302 	JSON values can have one of the types defined in the JSON.Type enum. They
303 	behave mostly like values in ECMA script in the way that you can
304 	transparently perform operations on them. However, strict typechecking is
305 	done, so that operations between differently typed JSON values will throw
306 	a JSONException. Additionally, an explicit cast or using get!() or to!() is
307 	required to convert a JSON value to the corresponding static D type.
308 */
309 align(8) // ensures that pointers stay on 64-bit boundaries on x64 so that they get scanned by the GC
310 struct JSON {
311 @safe:
312 
313   static assert(!hasElaborateDestructor!BigInt && !hasElaborateCopyConstructor!BigInt,
314       "struct JSON is missing required ~this and/or this(this) members for BigInt.");
315 
316   private {
317     // putting all fields in a union results in many false pointers leading to
318     // memory leaks and, worse, std.algorithm.swap triggering an assertion
319     // because of internal pointers. This crude workaround seems to fix
320     // the issues.
321     enum m_size = max((BigInt.sizeof + (void*).sizeof), 2);
322     // NOTE : DMD 2.067.1 doesn't seem to init void[] correctly on its own.
323     // Explicity initializing it works around this issue. Using a void[]
324     // array here to guarantee that it's scanned by the GC.
325     void[m_size] m_data = (void[m_size]).init;
326 
327     static assert(m_data.offsetof == 0, "m_data must be the first struct member.");
328     static assert(BigInt.alignof <= 8,
329         "JSON struct alignment of 8 isn't sufficient to store BigInt.");
330 
331     ref inout(T) getDataAs(T)() inout @trusted {
332       static assert(T.sizeof <= m_data.sizeof);
333       return (cast(inout(T)[1]) m_data[0 .. T.sizeof])[0];
334     }
335 
336     @property ref inout(BigInt) m_bigInt() inout {
337       return getDataAs!BigInt();
338     }
339 
340     @property ref inout(long) m_int() inout {
341       return getDataAs!long();
342     }
343 
344     @property ref inout(double) m_float() inout {
345       return getDataAs!double();
346     }
347 
348     @property ref inout(bool) m_bool() inout {
349       return getDataAs!bool();
350     }
351 
352     @property ref inout(string) m_string() inout {
353       return getDataAs!string();
354     }
355 
356     @property ref inout(JSON[string]) m_object() inout {
357       return getDataAs!(JSON[string])();
358     }
359 
360     @property ref inout(JSON[]) m_array() inout {
361       return getDataAs!(JSON[])();
362     }
363 
364     Type m_type = Type.undefined;
365 
366     version (VibeJSONFieldNames) {
367       string m_name;
368     }
369   }
370 
371   /** Represents the run time type of a JSON value.
372 	*/
373   enum Type {
374     undefined, /// A non-existent value in a JSON object
375     null_, /// Null value
376     bool_, /// Boolean value
377     int_, /// 64-bit integer value
378     bigInt, /// BigInt values
379     float_, /// 64-bit floating point value
380     string, /// UTF-8 string
381     array, /// Array of JSON values
382     object, /// JSON object aka. dictionary from string to JSON
383   }
384 
385   /// New JSON value of Type.Undefined
386   static @property JSON undefined() {
387     return JSON();
388   }
389 
390   /// New JSON value of Type.Object
391   static @property JSON emptyObject() {
392     return JSON(cast(JSON[string]) null);
393   }
394 
395   /// New JSON value of Type.Array
396   static @property JSON emptyArray() {
397     return JSON(cast(JSON[]) null);
398   }
399 
400   version (JSONLineNumbers) int line;
401 
402   /**
403 		Constructor for a JSON object.
404 	*/
405   this(typeof(null)) @trusted {
406     m_type = Type.null_;
407   }
408   /// ditto
409   this(bool v) @trusted {
410     m_type = Type.bool_;
411     m_bool = v;
412   }
413   /// ditto
414   this(byte v) {
415     this(cast(long) v);
416   }
417   /// ditto
418   this(ubyte v) {
419     this(cast(long) v);
420   }
421   /// ditto
422   this(short v) {
423     this(cast(long) v);
424   }
425   /// ditto
426   this(ushort v) {
427     this(cast(long) v);
428   }
429   /// ditto
430   this(int v) {
431     this(cast(long) v);
432   }
433   /// ditto
434   this(uint v) {
435     this(cast(long) v);
436   }
437   /// ditto
438   this(long v) @trusted {
439     m_type = Type.int_;
440     m_int = v;
441   }
442   /// ditto
443   this(BigInt v) @trusted {
444     m_type = Type.bigInt;
445     initBigInt();
446     m_bigInt = v;
447   }
448   /// ditto
449   this(double v) @trusted {
450     m_type = Type.float_;
451     m_float = v;
452   }
453   /// ditto
454   this(string v) @trusted {
455     m_type = Type..string;
456     m_string = v;
457   }
458   /// ditto
459   this(JSON[] v) @trusted {
460     m_type = Type.array;
461     m_array = v;
462   }
463   /// ditto
464   this(JSON[string] v) @trusted {
465     m_type = Type.object;
466     m_object = v;
467   }
468 
469   // used internally for UUID serialization support
470   private this(UUID v) {
471     this(v.toString());
472   }
473 
474   /**
475 		Converts a std.json.JSONValue object to a vibe JSON object.
476 	 */
477   this(in JSONValue value) @safe {
478     final switch (value.type) {
479     case JSONType.null_:
480       this = null;
481       break;
482     case JSONType.object:
483       this = emptyObject;
484       () @trusted {
485         foreach (string k, ref const JSONValue v; value.object)
486           this[k] = JSON(v);
487       }();
488       break;
489     case JSONType.array:
490       this = (() @trusted => JSON(value.array.map!(a => JSON(a)).array))();
491       break;
492     case JSONType..string:
493       this = value.str;
494       break;
495     case JSONType.integer:
496       this = value.integer;
497       break;
498     case JSONType.uinteger:
499       this = BigInt(value.uinteger);
500       break;
501     case JSONType.float_:
502       this = value.floating;
503       break;
504     case JSONType.true_:
505       this = true;
506       break;
507     case JSONType.false_:
508       this = false;
509       break;
510     }
511   }
512 
513   /**
514 		Allows assignment of D values to a JSON value.
515 	*/
516   ref JSON opAssign(JSON v) return  {
517     if (v.type != Type.bigInt)
518       runDestructors();
519     auto old_type = m_type;
520     m_type = v.m_type;
521     final switch (m_type) {
522     case Type.undefined:
523       m_string = null;
524       break;
525     case Type.null_:
526       m_string = null;
527       break;
528     case Type.bool_:
529       m_bool = v.m_bool;
530       break;
531     case Type.int_:
532       m_int = v.m_int;
533       break;
534     case Type.bigInt:
535       if (old_type != Type.bigInt)
536         initBigInt();
537       m_bigInt = v.m_bigInt;
538       break;
539     case Type.float_:
540       m_float = v.m_float;
541       break;
542     case Type..string:
543       m_string = v.m_string;
544       break;
545     case Type.array:
546       opAssign(v.m_array);
547       break;
548     case Type.object:
549       opAssign(v.m_object);
550       break;
551     }
552     return this;
553   }
554   /// ditto
555   void opAssign(typeof(null)) {
556     runDestructors();
557     m_type = Type.null_;
558     m_string = null;
559   }
560   /// ditto
561   bool opAssign(bool v) {
562     runDestructors();
563     m_type = Type.bool_;
564     m_bool = v;
565     return v;
566   }
567   /// ditto
568   int opAssign(int v) {
569     runDestructors();
570     m_type = Type.int_;
571     m_int = v;
572     return v;
573   }
574   /// ditto
575   long opAssign(long v) {
576     runDestructors();
577     m_type = Type.int_;
578     m_int = v;
579     return v;
580   }
581   /// ditto
582   BigInt opAssign(BigInt v) {
583     if (m_type != Type.bigInt)
584       initBigInt();
585     m_type = Type.bigInt;
586     m_bigInt = v;
587     return v;
588   }
589   /// ditto
590   double opAssign(double v) {
591     runDestructors();
592     m_type = Type.float_;
593     m_float = v;
594     return v;
595   }
596   /// ditto
597   string opAssign(string v) {
598     runDestructors();
599     m_type = Type..string;
600     m_string = v;
601     return v;
602   }
603   /// ditto
604   JSON[] opAssign(JSON[] v) {
605     runDestructors();
606     m_type = Type.array;
607     m_array = v;
608     version (VibeJSONFieldNames) {
609       foreach (idx, ref av; m_array)
610         av.m_name = format("%s[%s]", m_name, idx);
611     }
612     return v;
613   }
614   /// ditto
615   JSON[string] opAssign(JSON[string] v) {
616     runDestructors();
617     m_type = Type.object;
618     m_object = v;
619     version (VibeJSONFieldNames) {
620       foreach (key, ref av; m_object)
621         av.m_name = format("%s.%s", m_name, key);
622     }
623     return v;
624   }
625 
626   // used internally for UUID serialization support
627   private UUID opAssign(UUID v) {
628     opAssign(v.toString());
629     return v;
630   }
631 
632   /**
633 		Allows removal of values from Type.Object JSON objects.
634 	*/
635   void remove(string item) {
636     checkType!(JSON[string])();
637     m_object.remove(item);
638   }
639 
640   /**
641 		The current type id of this JSON object.
642 	*/
643   @property Type type() const @safe {
644     return m_type;
645   }
646 
647   /**
648 		Clones a JSON value recursively.
649 	*/
650   JSON clone() const {
651     final switch (m_type) {
652     case Type.undefined:
653       return JSON.undefined;
654     case Type.null_:
655       return JSON(null);
656     case Type.bool_:
657       return JSON(m_bool);
658     case Type.int_:
659       return JSON(m_int);
660     case Type.bigInt:
661       return JSON(m_bigInt);
662     case Type.float_:
663       return JSON(m_float);
664     case Type..string:
665       return JSON(m_string);
666     case Type.array:
667       JSON[] ret;
668       foreach (v; this.byValue)
669         ret ~= v.clone();
670 
671       return JSON(ret);
672     case Type.object:
673       auto ret = JSON.emptyObject;
674       foreach (name, v; this.byKeyValue)
675         ret[name] = v.clone();
676       return ret;
677     }
678   }
679 
680   /**
681 		Allows direct indexing of array typed JSON values.
682 	*/
683   ref inout(JSON) opIndex(size_t idx) inout {
684     checkType!(JSON[])();
685     return m_array[idx];
686   }
687 
688   ///
689   unittest {
690     JSON value = JSON.emptyArray;
691     value ~= 1;
692     value ~= true;
693     value ~= "foo";
694     assert(value[0] == 1);
695     assert(value[1] == true);
696     assert(value[2] == "foo");
697   }
698 
699   /**
700 		Allows direct indexing of object typed JSON values using a string as
701 		the key.
702 
703 		Returns an object of `Type.undefined` if the key was not found.
704 	*/
705   const(JSON) opIndex(string key) const {
706     checkType!(JSON[string])();
707     if (auto pv = key in m_object)
708       return *pv;
709     JSON ret = JSON.undefined;
710     ret.m_string = key;
711     version (VibeJSONFieldNames)
712       ret.m_name = format("%s.%s", m_name, key);
713     return ret;
714   }
715   /// ditto
716   ref JSON opIndex(string key) {
717     checkType!(JSON[string])();
718     if (auto pv = key in m_object)
719       return *pv;
720     if (m_object is null) {
721       m_object = ["": JSON.init];
722       m_object.remove("");
723     }
724     m_object[key] = JSON.init;
725     auto nv = key in m_object;
726     assert(m_object !is null);
727     assert(nv !is null, "Failed to insert key '" ~ key ~ "' into AA!?");
728     nv.m_type = Type.undefined;
729     assert(nv.type == Type.undefined);
730     nv.m_string = key;
731     version (VibeJSONFieldNames)
732       nv.m_name = format("%s.%s", m_name, key);
733     return *nv;
734   }
735 
736   ///
737   unittest {
738     JSON value = JSON.emptyObject;
739     value["a"] = 1;
740     value["b"] = true;
741     value["c"] = "foo";
742     assert(value["a"] == 1);
743     assert(value["b"] == true);
744     assert(value["c"] == "foo");
745     assert(value["not-existing"].type() == Type.undefined);
746   }
747 
748   /**
749 		Returns a slice of a JSON array.
750 	*/
751   inout(JSON[]) opSlice() inout {
752     checkType!(JSON[])();
753     return m_array;
754   }
755   ///
756   inout(JSON[]) opSlice(size_t from, size_t to) inout {
757     checkType!(JSON[])();
758     return m_array[from .. to];
759   }
760 
761   /**
762 		Returns the number of entries of string, array or object typed JSON values.
763 	*/
764   @property size_t length() const @trusted {
765     checkType!(string, JSON[], JSON[string])("property length");
766     switch (m_type) {
767     case Type..string:
768       return m_string.length;
769     case Type.array:
770       return m_array.length;
771     case Type.object:
772       return m_object.length;
773     default:
774       assert(false);
775     }
776   }
777 
778   /**
779 		Allows foreach iterating over JSON objects and arrays.
780 	*/
781   int opApply(scope int delegate(ref JSON obj) del) @system {
782     checkType!(JSON[], JSON[string])("opApply");
783     if (m_type == Type.array) {
784       foreach (ref v; m_array)
785         if (auto ret = del(v))
786           return ret;
787       return 0;
788     } else {
789       foreach (ref v; m_object)
790         if (v.type != Type.undefined)
791           if (auto ret = del(v))
792             return ret;
793       return 0;
794     }
795   }
796   /// ditto
797   int opApply(scope int delegate(ref const JSON obj) del) const @system {
798     checkType!(JSON[], JSON[string])("opApply");
799     if (m_type == Type.array) {
800       foreach (ref v; m_array)
801         if (auto ret = del(v))
802           return ret;
803       return 0;
804     } else {
805       foreach (ref v; m_object)
806         if (v.type != Type.undefined)
807           if (auto ret = del(v))
808             return ret;
809       return 0;
810     }
811   }
812   /// ditto
813   int opApply(scope int delegate(ref size_t idx, ref JSON obj) del) @system {
814     checkType!(JSON[])("opApply");
815     foreach (idx, ref v; m_array)
816       if (auto ret = del(idx, v))
817         return ret;
818     return 0;
819   }
820   /// ditto
821   int opApply(scope int delegate(ref size_t idx, ref const JSON obj) del) const @system {
822     checkType!(JSON[])("opApply");
823     foreach (idx, ref v; m_array)
824       if (auto ret = del(idx, v))
825         return ret;
826     return 0;
827   }
828   /// ditto
829   int opApply(scope int delegate(ref string idx, ref JSON obj) del) @system {
830     checkType!(JSON[string])("opApply");
831     foreach (idx, ref v; m_object)
832       if (v.type != Type.undefined)
833         if (auto ret = del(idx, v))
834           return ret;
835     return 0;
836   }
837   /// ditto
838   int opApply(scope int delegate(ref string idx, ref const JSON obj) del) const @system {
839     checkType!(JSON[string])("opApply");
840     foreach (idx, ref v; m_object)
841       if (v.type != Type.undefined)
842         if (auto ret = del(idx, v))
843           return ret;
844     return 0;
845   }
846 
847   private alias KeyValue = Tuple!(string, "key", JSON, "value");
848 
849   /// Iterates over all key/value pairs of an object.
850   @property auto byKeyValue() @trusted {
851     checkType!(JSON[string])("byKeyValue");
852     return m_object.byKeyValue.map!(kv => KeyValue(kv.key, kv.value)).trustedRange;
853   }
854   /// ditto
855   @property auto byKeyValue() const @trusted {
856     checkType!(JSON[string])("byKeyValue");
857     return m_object.byKeyValue.map!(kv => const(KeyValue)(kv.key, kv.value)).trustedRange;
858   }
859   /// Iterates over all index/value pairs of an array.
860   @property auto byIndexValue() {
861     checkType!(JSON[])("byIndexValue");
862     return zip(iota(0, m_array.length), m_array);
863   }
864   /// ditto
865   @property auto byIndexValue() const {
866     checkType!(JSON[])("byIndexValue");
867     return zip(iota(0, m_array.length), m_array);
868   }
869   /// Iterates over all values of an object or array.
870   @property auto byValue() @trusted {
871     checkType!(JSON[], JSON[string])("byValue");
872     static struct Rng {
873       private {
874         bool isArray;
875         JSON[] array;
876         typeof(JSON.init.m_object.byValue) object;
877       }
878 
879       bool empty() @trusted {
880         if (isArray)
881           return array.length == 0;
882         else
883           return object.empty;
884       }
885 
886       auto front() @trusted {
887         if (isArray)
888           return array[0];
889         else
890           return object.front;
891       }
892 
893       void popFront() @trusted {
894         if (isArray)
895           array = array[1 .. $];
896         else
897           object.popFront();
898       }
899     }
900 
901     if (m_type == Type.array)
902       return Rng(true, m_array);
903     else
904       return Rng(false, null, m_object.byValue);
905   }
906   /// ditto
907   @property auto byValue() const @trusted {
908     checkType!(JSON[], JSON[string])("byValue");
909     static struct Rng {
910     @safe:
911       private {
912         bool isArray;
913         const(JSON)[] array;
914         typeof(const(JSON).init.m_object.byValue) object;
915       }
916 
917       bool empty() @trusted {
918         if (isArray)
919           return array.length == 0;
920         else
921           return object.empty;
922       }
923 
924       auto front() @trusted {
925         if (isArray)
926           return array[0];
927         else
928           return object.front;
929       }
930 
931       void popFront() @trusted {
932         if (isArray)
933           array = array[1 .. $];
934         else
935           object.popFront();
936       }
937     }
938 
939     if (m_type == Type.array)
940       return Rng(true, m_array);
941     else
942       return Rng(false, null, m_object.byValue);
943   }
944 
945   /**
946 		Converts this JSON object to a std.json.JSONValue object
947 	 */
948   T opCast(T)() const @safe if (is(T == JSONValue)) {
949     final switch (type) {
950     case JSON.Type.undefined:
951     case JSON.Type.null_:
952       return JSONValue(null);
953     case JSON.Type.bool_:
954       return JSONValue(get!bool);
955     case JSON.Type.int_:
956       return JSONValue(get!long);
957     case JSON.Type.bigInt:
958       auto bi = get!BigInt;
959       if (bi > long.max)
960         return JSONValue((() @trusted => cast(ulong) get!BigInt)());
961       else
962         return JSONValue((() @trusted => cast(long) get!BigInt)());
963     case JSON.Type.float_:
964       return JSONValue(get!double);
965     case JSON.Type..string:
966       return JSONValue(get!string);
967     case JSON.Type.array:
968       JSONValue[] ret;
969       foreach (ref const JSON e; byValue)
970         ret ~= cast(JSONValue) e;
971       return JSONValue(ret);
972     case JSON.Type.object:
973       JSONValue[string] ret;
974       foreach (string k, ref const JSON e; byKeyValue) {
975         if (e.type == JSON.Type.undefined)
976           continue;
977         ret[k] = cast(JSONValue) e;
978       }
979       return JSONValue(ret);
980     }
981   }
982 
983   /**
984 		Converts the JSON value to the corresponding D type - types must match exactly.
985 
986 		Available_Types:
987 			$(UL
988 				$(LI `bool` (`Type.bool_`))
989 				$(LI `double` (`Type.float_`))
990 				$(LI `float` (Converted from `double`))
991 				$(LI `long` (`Type.int_`))
992 				$(LI `ulong`, `int`, `uint`, `short`, `ushort`, `byte`, `ubyte` (Converted from `long`))
993 				$(LI `string` (`Type.string`))
994 				$(LI `JSON[]` (`Type.array`))
995 				$(LI `JSON[string]` (`Type.object`))
996 			)
997 
998 		See_Also: `opt`, `to`, `deserializeJSON`
999 	*/
1000   inout(T) opCast(T)() inout if (!is(T == JSONValue)) {
1001     return get!T;
1002   }
1003   /// ditto
1004   @property inout(T) get(T)() inout @trusted {
1005     static if (!is(T : bool) && is(T : long))
1006       checkType!(long, BigInt)();
1007     else
1008       checkType!T();
1009 
1010     static if (is(T == bool))
1011       return m_bool;
1012     else static if (is(T == double))
1013       return m_float;
1014     else static if (is(T == float))
1015       return cast(T) m_float;
1016     else static if (is(T == string))
1017       return m_string;
1018     else static if (is(T == UUID))
1019       return UUID(m_string);
1020     else static if (is(T == JSON[]))
1021       return m_array;
1022     else static if (is(T == JSON[string]))
1023       return m_object;
1024     else static if (is(T == BigInt))
1025       return m_type == Type.bigInt ? m_bigInt : BigInt(m_int);
1026     else static if (is(T : long)) {
1027       if (m_type == Type.bigInt) {
1028         enforceJSON(m_bigInt <= T.max && m_bigInt >= T.min,
1029             "Integer conversion out of bounds error");
1030         return cast(T) m_bigInt.toLong();
1031       } else {
1032         enforceJSON(m_int <= T.max && m_int >= T.min, "Integer conversion out of bounds error");
1033         return cast(T) m_int;
1034       }
1035     } else
1036       static assert(0,
1037           "JSON can only be cast to (bool, long, std.bigint.BigInt, double, string, JSON[] or JSON[string]. Not "
1038           ~ T.stringof ~ ".");
1039   }
1040 
1041   /**
1042 		Returns the native type for this JSON if it matches the current runtime type.
1043 
1044 		If the runtime type does not match the given native type, the 'def' parameter is returned
1045 		instead.
1046 
1047 		See_Also: `get`
1048 	*/
1049   @property const(T) opt(T)(const(T) def = T.init) const {
1050     if (typeId!T != m_type)
1051       return def;
1052     return get!T;
1053   }
1054   /// ditto
1055   @property T opt(T)(T def = T.init) {
1056     if (typeId!T != m_type)
1057       return def;
1058     return get!T;
1059   }
1060 
1061   /**
1062 		Converts the JSON value to the corresponding D type - types are converted as necessary.
1063 
1064 		Automatically performs conversions between strings and numbers. See
1065 		`get` for the list of available types. For converting/deserializing
1066 		JSON to complex data types see `deserializeJSON`.
1067 
1068 		See_Also: `get`, `deserializeJSON`
1069 	*/
1070   @property inout(T) to(T)() inout {
1071     static if (is(T == bool)) {
1072       final switch (m_type) {
1073       case Type.undefined:
1074         return false;
1075       case Type.null_:
1076         return false;
1077       case Type.bool_:
1078         return m_bool;
1079       case Type.int_:
1080         return m_int != 0;
1081       case Type.bigInt:
1082         return m_bigInt != 0;
1083       case Type.float_:
1084         return m_float != 0;
1085       case Type..string:
1086         return m_string.length > 0;
1087       case Type.array:
1088         return m_array.length > 0;
1089       case Type.object:
1090         return m_object.length > 0;
1091       }
1092     } else static if (is(T == double)) {
1093       final switch (m_type) {
1094       case Type.undefined:
1095         return T.init;
1096       case Type.null_:
1097         return 0;
1098       case Type.bool_:
1099         return m_bool ? 1 : 0;
1100       case Type.int_:
1101         return m_int;
1102       case Type.bigInt:
1103         return bigIntToLong();
1104       case Type.float_:
1105         return m_float;
1106       case Type..string:
1107         return .to!double(cast(string) m_string);
1108       case Type.array:
1109         return double.init;
1110       case Type.object:
1111         return double.init;
1112       }
1113     } else static if (is(T == float)) {
1114       final switch (m_type) {
1115       case Type.undefined:
1116         return T.init;
1117       case Type.null_:
1118         return 0;
1119       case Type.bool_:
1120         return m_bool ? 1 : 0;
1121       case Type.int_:
1122         return m_int;
1123       case Type.bigInt:
1124         return bigIntToLong();
1125       case Type.float_:
1126         return m_float;
1127       case Type..string:
1128         return .to!float(cast(string) m_string);
1129       case Type.array:
1130         return float.init;
1131       case Type.object:
1132         return float.init;
1133       }
1134     } else static if (is(T == long)) {
1135       final switch (m_type) {
1136       case Type.undefined:
1137         return 0;
1138       case Type.null_:
1139         return 0;
1140       case Type.bool_:
1141         return m_bool ? 1 : 0;
1142       case Type.int_:
1143         return m_int;
1144       case Type.bigInt:
1145         return cast(long) bigIntToLong();
1146       case Type.float_:
1147         return cast(long) m_float;
1148       case Type..string:
1149         return .to!long(m_string);
1150       case Type.array:
1151         return 0;
1152       case Type.object:
1153         return 0;
1154       }
1155     } else static if (is(T : long)) {
1156       final switch (m_type) {
1157       case Type.undefined:
1158         return 0;
1159       case Type.null_:
1160         return 0;
1161       case Type.bool_:
1162         return m_bool ? 1 : 0;
1163       case Type.int_:
1164         return cast(T) m_int;
1165       case Type.bigInt:
1166         return cast(T) bigIntToLong();
1167       case Type.float_:
1168         return cast(T) m_float;
1169       case Type..string:
1170         return cast(T).to!long(cast(string) m_string);
1171       case Type.array:
1172         return 0;
1173       case Type.object:
1174         return 0;
1175       }
1176     } else static if (is(T == string)) {
1177       switch (m_type) {
1178       default:
1179         return toString();
1180       case Type..string:
1181         return m_string;
1182       }
1183     } else static if (is(T == JSON[])) {
1184       switch (m_type) {
1185       default:
1186         return JSON([this]);
1187       case Type.array:
1188         return m_array;
1189       }
1190     } else static if (is(T == JSON[string])) {
1191       switch (m_type) {
1192       default:
1193         return JSON(["value": this]);
1194       case Type.object:
1195         return m_object;
1196       }
1197     } else static if (is(T == BigInt)) {
1198       final switch (m_type) {
1199       case Type.undefined:
1200         return BigInt(0);
1201       case Type.null_:
1202         return BigInt(0);
1203       case Type.bool_:
1204         return BigInt(m_bool ? 1 : 0);
1205       case Type.int_:
1206         return BigInt(m_int);
1207       case Type.bigInt:
1208         return m_bigInt;
1209       case Type.float_:
1210         return BigInt(cast(long) m_float);
1211       case Type..string:
1212         return BigInt(.to!long(m_string));
1213       case Type.array:
1214         return BigInt(0);
1215       case Type.object:
1216         return BigInt(0);
1217       }
1218     } else static if (is(T == JSONValue)) {
1219       return cast(JSONValue) this;
1220     } else
1221       static assert(0,
1222           "JSON can only be cast to (bool, long, std.bigint.BigInt, double, string, JSON[] or JSON[string]. Not "
1223           ~ T.stringof ~ ".");
1224   }
1225 
1226   /**
1227 		Performs unary operations on the JSON value.
1228 
1229 		The following operations are supported for each type:
1230 
1231 		$(DL
1232 			$(DT Null)   $(DD none)
1233 			$(DT Bool)   $(DD ~)
1234 			$(DT Int)    $(DD +, -, ++, --)
1235 			$(DT Float)  $(DD +, -, ++, --)
1236 			$(DT String) $(DD none)
1237 			$(DT Array)  $(DD none)
1238 			$(DT Object) $(DD none)
1239 		)
1240 	*/
1241   JSON opUnary(string op)() const {
1242     static if (op == "~") {
1243       checkType!bool();
1244       return JSON(~m_bool);
1245     } else static if (op == "+" || op == "-" || op == "++" || op == "--") {
1246       checkType!(BigInt, long, double)("unary " ~ op);
1247       if (m_type == Type.int_)
1248         mixin("return JSON(" ~ op ~ "m_int);");
1249       else if (m_type == Type.bigInt)
1250         mixin("return JSON(" ~ op ~ "m_bigInt);");
1251       else if (m_type == Type.float_)
1252         mixin("return JSON(" ~ op ~ "m_float);");
1253       else
1254         assert(false);
1255     } else
1256       static assert(0, "Unsupported operator '" ~ op ~ "' for type JSON.");
1257   }
1258   /**
1259 		Performs binary operations between JSON values.
1260 
1261 		The two JSON values must be of the same run time type or a JSONException
1262 		will be thrown. Only the operations listed are allowed for each of the
1263 		types.
1264 
1265 		$(DL
1266 			$(DT Null)   $(DD none)
1267 			$(DT Bool)   $(DD &&, ||)
1268 			$(DT Int)    $(DD +, -, *, /, %)
1269 			$(DT Float)  $(DD +, -, *, /, %)
1270 			$(DT String) $(DD ~)
1271 			$(DT Array)  $(DD ~)
1272 			$(DT Object) $(DD in)
1273 		)
1274 	*/
1275   JSON opBinary(string op)(ref const(JSON) other) const {
1276     enforceJSON(m_type == other.m_type,
1277         "Binary operation '" ~ op ~ "' between " ~ .to!string(
1278           m_type) ~ " and " ~ .to!string(other.m_type) ~ " JSON objects.");
1279     static if (op == "&&") {
1280       checkType!(bool)(op);
1281       return JSON(m_bool && other.m_bool);
1282     } else static if (op == "||") {
1283       checkType!(bool)(op);
1284       return JSON(m_bool || other.m_bool);
1285     } else static if (op == "+") {
1286       checkType!(BigInt, long, double)(op);
1287       if (m_type == Type.int_)
1288         return JSON(m_int + other.m_int);
1289       else if (m_type == Type.bigInt)
1290         return JSON(() @trusted { return m_bigInt + other.m_bigInt; }());
1291       else if (m_type == Type.float_)
1292         return JSON(m_float + other.m_float);
1293       else
1294         assert(false);
1295     } else static if (op == "-") {
1296       checkType!(BigInt, long, double)(op);
1297       if (m_type == Type.int_)
1298         return JSON(m_int - other.m_int);
1299       else if (m_type == Type.bigInt)
1300         return JSON(() @trusted { return m_bigInt - other.m_bigInt; }());
1301       else if (m_type == Type.float_)
1302         return JSON(m_float - other.m_float);
1303       else
1304         assert(false);
1305     } else static if (op == "*") {
1306       checkType!(BigInt, long, double)(op);
1307       if (m_type == Type.int_)
1308         return JSON(m_int * other.m_int);
1309       else if (m_type == Type.bigInt)
1310         return JSON(() @trusted { return m_bigInt * other.m_bigInt; }());
1311       else if (m_type == Type.float_)
1312         return JSON(m_float * other.m_float);
1313       else
1314         assert(false);
1315     } else static if (op == "/") {
1316       checkType!(BigInt, long, double)(op);
1317       if (m_type == Type.int_)
1318         return JSON(m_int / other.m_int);
1319       else if (m_type == Type.bigInt)
1320         return JSON(() @trusted { return m_bigInt / other.m_bigInt; }());
1321       else if (m_type == Type.float_)
1322         return JSON(m_float / other.m_float);
1323       else
1324         assert(false);
1325     } else static if (op == "%") {
1326       checkType!(BigInt, long, double)(op);
1327       if (m_type == Type.int_)
1328         return JSON(m_int % other.m_int);
1329       else if (m_type == Type.bigInt)
1330         return JSON(() @trusted { return m_bigInt % other.m_bigInt; }());
1331       else if (m_type == Type.float_)
1332         return JSON(m_float % other.m_float);
1333       else
1334         assert(false);
1335     } else static if (op == "~") {
1336       checkType!(string, JSON[])(op);
1337       if (m_type == Type..string)
1338         return JSON(m_string ~ other.m_string);
1339       else if (m_type == Type.array)
1340         return JSON(m_array ~ other.m_array);
1341       else
1342         assert(false);
1343     } else
1344       static assert(0, "Unsupported operator '" ~ op ~ "' for type JSON.");
1345   }
1346   /// ditto
1347   JSON opBinary(string op)(JSON other) if (op == "~") {
1348     static if (op == "~") {
1349       checkType!(string, JSON[])(op);
1350       if (m_type == Type..string)
1351         return JSON(m_string ~ other.m_string);
1352       else if (m_type == Type.array)
1353         return JSON(m_array ~ other.m_array);
1354       else
1355         assert(false);
1356     } else
1357       static assert(0, "Unsupported operator '" ~ op ~ "' for type JSON.");
1358   }
1359   /// ditto
1360   void opOpAssign(string op)(JSON other)
1361       if (op == "+" || op == "-" || op == "*" || op == "/" || op == "%" || op == "~") {
1362     enforceJSON(m_type == other.m_type || op == "~" && m_type == Type.array,
1363         "Binary operation '" ~ op ~ "=' between " ~ .to!string(
1364           m_type) ~ " and " ~ .to!string(other.m_type) ~ " JSON objects.");
1365     static if (op == "+") {
1366       if (m_type == Type.int_)
1367         m_int += other.m_int;
1368       else if (m_type == Type.bigInt)
1369         m_bigInt += other.m_bigInt;
1370       else if (m_type == Type.float_)
1371         m_float += other.m_float;
1372       else
1373         enforceJSON(false, "'+=' only allowed for scalar types, not " ~ .to!string(m_type) ~ ".");
1374     } else static if (op == "-") {
1375       if (m_type == Type.int_)
1376         m_int -= other.m_int;
1377       else if (m_type == Type.bigInt)
1378         m_bigInt -= other.m_bigInt;
1379       else if (m_type == Type.float_)
1380         m_float -= other.m_float;
1381       else
1382         enforceJSON(false, "'-=' only allowed for scalar types, not " ~ .to!string(m_type) ~ ".");
1383     } else static if (op == "*") {
1384       if (m_type == Type.int_)
1385         m_int *= other.m_int;
1386       else if (m_type == Type.bigInt)
1387         m_bigInt *= other.m_bigInt;
1388       else if (m_type == Type.float_)
1389         m_float *= other.m_float;
1390       else
1391         enforceJSON(false, "'*=' only allowed for scalar types, not " ~ .to!string(m_type) ~ ".");
1392     } else static if (op == "/") {
1393       if (m_type == Type.int_)
1394         m_int /= other.m_int;
1395       else if (m_type == Type.bigInt)
1396         m_bigInt /= other.m_bigInt;
1397       else if (m_type == Type.float_)
1398         m_float /= other.m_float;
1399       else
1400         enforceJSON(false, "'/=' only allowed for scalar types, not " ~ .to!string(m_type) ~ ".");
1401     } else static if (op == "%") {
1402       if (m_type == Type.int_)
1403         m_int %= other.m_int;
1404       else if (m_type == Type.bigInt)
1405         m_bigInt %= other.m_bigInt;
1406       else if (m_type == Type.float_)
1407         m_float %= other.m_float;
1408       else
1409         enforceJSON(false, "'%=' only allowed for scalar types, not " ~ .to!string(m_type) ~ ".");
1410     } else static if (op == "~") {
1411       if (m_type == Type..string)
1412         m_string ~= other.m_string;
1413       else if (m_type == Type.array) {
1414         if (other.m_type == Type.array)
1415           m_array ~= other.m_array;
1416         else
1417           appendArrayElement(other);
1418       } else
1419         enforceJSON(false,
1420             "'~=' only allowed for string and array types, not " ~ .to!string(m_type) ~ ".");
1421     } else
1422       static assert(0, "Unsupported operator '" ~ op ~ "=' for type JSON.");
1423   }
1424   /// ditto
1425   void opOpAssign(string op, T)(T other)
1426       if (!is(T == JSON) && is(typeof(JSON(other)))) {
1427     opOpAssign!op(JSON(other));
1428   }
1429   /// ditto
1430   JSON opBinary(string op)(bool other) const {
1431     checkType!bool();
1432     mixin("return JSON(m_bool " ~ op ~ " other);");
1433   }
1434   /// ditto
1435   JSON opBinary(string op)(long other) const {
1436     checkType!(long, BigInt)();
1437     if (m_type == Type.bigInt)
1438       mixin("return JSON(m_bigInt " ~ op ~ " other);");
1439     else
1440       mixin("return JSON(m_int " ~ op ~ " other);");
1441   }
1442   /// ditto
1443   JSON opBinary(string op)(BigInt other) const {
1444     checkType!(long, BigInt)();
1445     if (m_type == Type.bigInt)
1446       mixin("return JSON(m_bigInt " ~ op ~ " other);");
1447     else
1448       mixin("return JSON(m_int " ~ op ~ " other);");
1449   }
1450   /// ditto
1451   JSON opBinary(string op)(double other) const {
1452     checkType!double();
1453     mixin("return JSON(m_float " ~ op ~ " other);");
1454   }
1455   /// ditto
1456   JSON opBinary(string op)(string other) const {
1457     checkType!string();
1458     mixin("return JSON(m_string " ~ op ~ " other);");
1459   }
1460   /// ditto
1461   JSON opBinary(string op)(JSON[] other) {
1462     checkType!(JSON[])();
1463     mixin("return JSON(m_array " ~ op ~ " other);");
1464   }
1465   /// ditto
1466   JSON opBinaryRight(string op)(bool other) const {
1467     checkType!bool();
1468     mixin("return JSON(other " ~ op ~ " m_bool);");
1469   }
1470   /// ditto
1471   JSON opBinaryRight(string op)(long other) const {
1472     checkType!(long, BigInt)();
1473     if (m_type == Type.bigInt)
1474       mixin("return JSON(other " ~ op ~ " m_bigInt);");
1475     else
1476       mixin("return JSON(other " ~ op ~ " m_int);");
1477   }
1478   /// ditto
1479   JSON opBinaryRight(string op)(BigInt other) const {
1480     checkType!(long, BigInt)();
1481     if (m_type == Type.bigInt)
1482       mixin("return JSON(other " ~ op ~ " m_bigInt);");
1483     else
1484       mixin("return JSON(other " ~ op ~ " m_int);");
1485   }
1486   /// ditto
1487   JSON opBinaryRight(string op)(double other) const {
1488     checkType!double();
1489     mixin("return JSON(other " ~ op ~ " m_float);");
1490   }
1491   /// ditto
1492   JSON opBinaryRight(string op)(string other) const if (op == "~") {
1493     checkType!string();
1494     return JSON(other ~ m_string);
1495   }
1496   /// ditto
1497   JSON opBinaryRight(string op)(JSON[] other) {
1498     checkType!(JSON[])();
1499     mixin("return JSON(other " ~ op ~ " m_array);");
1500   }
1501 
1502   /** Checks wheter a particular key is set and returns a pointer to it.
1503 
1504 		For field that don't exist or have a type of `Type.undefined`,
1505 		the `in` operator will return `null`.
1506 	*/
1507   inout(JSON)* opBinaryRight(string op)(string other) inout if (op == "in") {
1508     checkType!(JSON[string])();
1509     auto pv = other in m_object;
1510     if (!pv)
1511       return null;
1512     if (pv.type == Type.undefined)
1513       return null;
1514     return pv;
1515   }
1516 
1517   ///
1518   unittest {
1519     auto j = JSON.emptyObject;
1520     j["a"] = "foo";
1521     j["b"] = JSON.undefined;
1522 
1523     assert("a" in j);
1524     assert(("a" in j).get!string == "foo");
1525     assert("b" !in j);
1526     assert("c" !in j);
1527   }
1528 
1529   /**
1530 	 * The append operator will append arrays. This method always appends it's argument as an array element, so nested arrays can be created.
1531 	 */
1532   void appendArrayElement(JSON element) {
1533     enforceJSON(m_type == Type.array,
1534         "'appendArrayElement' only allowed for array types, not " ~ .to!string(m_type) ~ ".");
1535     m_array ~= element;
1536   }
1537 
1538   /**
1539 		Compares two JSON values for equality.
1540 
1541 		If the two values have different types, they are considered unequal.
1542 		This differs with ECMA script, which performs a type conversion before
1543 		comparing the values.
1544 	*/
1545 
1546   bool opEquals(ref const JSON other) const {
1547     if (m_type != other.m_type)
1548       return false;
1549     final switch (m_type) {
1550     case Type.undefined:
1551       return false;
1552     case Type.null_:
1553       return true;
1554     case Type.bool_:
1555       return m_bool == other.m_bool;
1556     case Type.int_:
1557       return m_int == other.m_int;
1558     case Type.bigInt:
1559       return m_bigInt == other.m_bigInt;
1560     case Type.float_:
1561       return m_float == other.m_float;
1562     case Type..string:
1563       return m_string == other.m_string;
1564     case Type.array:
1565       return m_array == other.m_array;
1566     case Type.object:
1567       return m_object == other.m_object;
1568     }
1569   }
1570   /// ditto
1571   bool opEquals(const JSON other) const {
1572     return opEquals(other);
1573   }
1574   /// ditto
1575   bool opEquals(typeof(null)) const {
1576     return m_type == Type.null_;
1577   }
1578   /// ditto
1579   bool opEquals(bool v) const {
1580     return m_type == Type.bool_ && m_bool == v;
1581   }
1582   /// ditto
1583   bool opEquals(int v) const {
1584     return (m_type == Type.int_ && m_int == v) || (m_type == Type.bigInt && m_bigInt == v);
1585   }
1586   /// ditto
1587   bool opEquals(long v) const {
1588     return (m_type == Type.int_ && m_int == v) || (m_type == Type.bigInt && m_bigInt == v);
1589   }
1590   /// ditto
1591   bool opEquals(BigInt v) const {
1592     return (m_type == Type.int_ && m_int == v) || (m_type == Type.bigInt && m_bigInt == v);
1593   }
1594   /// ditto
1595   bool opEquals(double v) const {
1596     return m_type == Type.float_ && m_float == v;
1597   }
1598   /// ditto
1599   bool opEquals(string v) const {
1600     return m_type == Type..string && m_string == v;
1601   }
1602 
1603   /**
1604 		Compares two JSON values.
1605 
1606 		If the types of the two values differ, the value with the smaller type
1607 		id is considered the smaller value. This differs from ECMA script, which
1608 		performs a type conversion before comparing the values.
1609 
1610 		JSON values of type Object cannot be compared and will throw an
1611 		exception.
1612 	*/
1613   int opCmp(ref const JSON other) const {
1614     if (m_type != other.m_type)
1615       return m_type < other.m_type ? -1 : 1;
1616     final switch (m_type) {
1617     case Type.undefined:
1618       return 0;
1619     case Type.null_:
1620       return 0;
1621     case Type.bool_:
1622       return m_bool < other.m_bool ? -1 : m_bool == other.m_bool ? 0 : 1;
1623     case Type.int_:
1624       return m_int < other.m_int ? -1 : m_int == other.m_int ? 0 : 1;
1625     case Type.bigInt:
1626       return () @trusted { return m_bigInt < other.m_bigInt; }() ? -1 : m_bigInt == other.m_bigInt
1627         ? 0 : 1;
1628     case Type.float_:
1629       return m_float < other.m_float ? -1 : m_float == other.m_float ? 0 : 1;
1630     case Type..string:
1631       return m_string < other.m_string ? -1 : m_string == other.m_string ? 0 : 1;
1632     case Type.array:
1633       return m_array < other.m_array ? -1 : m_array == other.m_array ? 0 : 1;
1634     case Type.object:
1635       enforceJSON(false, "JSON objects cannot be compared.");
1636       assert(false);
1637     }
1638   }
1639 
1640   alias opDollar = length;
1641 
1642   /**
1643 		Returns the type id corresponding to the given D type.
1644 	*/
1645   static @property Type typeId(T)() {
1646     static if (is(T == typeof(null)))
1647       return Type.null_;
1648     else static if (is(T == bool))
1649       return Type.bool_;
1650     else static if (is(T == double))
1651       return Type.float_;
1652     else static if (is(T == float))
1653       return Type.float_;
1654     else static if (is(T : long))
1655       return Type.int_;
1656     else static if (is(T == string))
1657       return Type..string;
1658     else static if (is(T == UUID))
1659       return Type..string;
1660     else static if (is(T == JSON[]))
1661       return Type.array;
1662     else static if (is(T == JSON[string]))
1663       return Type.object;
1664     else static if (is(T == BigInt))
1665       return Type.bigInt;
1666     else
1667       static assert(false, "Unsupported JSON type '" ~ T.stringof
1668           ~ "'. Only bool, long, std.bigint.BigInt, double, string, JSON[] and JSON[string] are allowed.");
1669   }
1670 
1671   /**
1672 		Returns the JSON object as a string.
1673 
1674 		For large JSON values use writeJSONString instead as this function will store the whole string
1675 		in memory, whereas writeJSONString writes it out bit for bit.
1676 
1677 		See_Also: writeJSONString, toPrettyString
1678 	*/
1679   string toString() const @trusted {
1680     // DMD BUG: this should actually be all @safe, but for some reason
1681     // @safe inference for writeJSONString doesn't work.
1682     auto ret = appender!string();
1683     writeJSONString(ret, this);
1684     return ret.data;
1685   }
1686   /// ditto
1687   void toString(scope void delegate(const(char)[]) @safe sink, FormatSpec!char fmt) @trusted {
1688     // DMD BUG: this should actually be all @safe, but for some reason
1689     // @safe inference for writeJSONString doesn't work.
1690     static struct DummyRangeS {
1691       void delegate(const(char)[]) @safe sink;
1692       void put(const(char)[] str) @safe {
1693         sink(str);
1694       }
1695 
1696       void put(char ch) @trusted {
1697         sink((&ch)[0 .. 1]);
1698       }
1699     }
1700 
1701     auto r = DummyRangeS(sink);
1702     writeJSONString(r, this);
1703   }
1704   /// ditto
1705   void toString(scope void delegate(const(char)[]) @system sink, FormatSpec!char fmt) @system {
1706     // DMD BUG: this should actually be all @safe, but for some reason
1707     // @safe inference for writeJSONString doesn't work.
1708     static struct DummyRange {
1709       void delegate(const(char)[]) sink;
1710     @trusted:
1711       void put(const(char)[] str) {
1712         sink(str);
1713       }
1714 
1715       void put(char ch) {
1716         sink((&ch)[0 .. 1]);
1717       }
1718     }
1719 
1720     auto r = DummyRange(sink);
1721     writeJSONString(r, this);
1722   }
1723 
1724   /**
1725 		Returns the JSON object as a "pretty" string.
1726 
1727 		---
1728 		auto JSON = JSON(["foo": JSON("bar")]);
1729 		writeln(JSON.toPrettyString());
1730 
1731 		// output:
1732 		// {
1733 		//     "foo": "bar"
1734 		// }
1735 		---
1736 
1737 		Params:
1738 			level = Specifies the base amount of indentation for the output. Indentation  is always
1739 				done using tab characters.
1740 
1741 		See_Also: writePrettyJSONString, toString
1742 	*/
1743   string toPrettyString(int level = 0) const @trusted {
1744     auto ret = appender!string();
1745     writePrettyJSONString(ret, this, level);
1746     return ret.data;
1747   }
1748 
1749   private void checkType(TYPES...)(string op = null) const {
1750     bool matched = false;
1751     foreach (T; TYPES)
1752       if (m_type == typeId!T)
1753         matched = true;
1754     if (matched)
1755       return;
1756 
1757     string name;
1758     version (VibeJSONFieldNames) {
1759       if (m_name.length)
1760         name = m_name ~ " of type " ~ m_type.to!string;
1761       else
1762         name = "JSON of type " ~ m_type.to!string;
1763     } else
1764       name = "JSON of type " ~ m_type.to!string;
1765 
1766     string expected;
1767     static if (TYPES.length == 1)
1768       expected = typeId!(TYPES[0]).to!string;
1769     else {
1770       foreach (T; TYPES) {
1771         if (expected.length > 0)
1772           expected ~= ", ";
1773         expected ~= typeId!T.to!string;
1774       }
1775     }
1776 
1777     if (!op.length)
1778       throw new JSONException(format("Got %s, expected %s.", name, expected));
1779     else
1780       throw new JSONException(format("Got %s, expected %s for %s.", name, expected, op));
1781   }
1782 
1783   private void initBigInt() @trusted {
1784     BigInt[1] init_;
1785     // BigInt is a struct, and it has a special BigInt.init value, which differs from null.
1786     // m_data has no special initializer and when it tries to first access to BigInt
1787     // via m_bigInt(), we should explicitly initialize m_data with BigInt.init
1788     m_data[0 .. BigInt.sizeof] = cast(void[]) init_;
1789   }
1790 
1791   private void runDestructors() {
1792     if (m_type != Type.bigInt)
1793       return;
1794 
1795     BigInt init_;
1796     // After swaping, init_ contains the real number from JSON, and it
1797     // will be destroyed when this function is finished.
1798     // m_bigInt now contains static BigInt.init value and destruction may
1799     // be ommited for it.
1800     swap(init_, m_bigInt);
1801   }
1802 
1803   private long bigIntToLong() inout {
1804     assert(m_type == Type.bigInt,
1805         format("Converting non-bigInt type with bitIntToLong!?: %s", cast(Type) m_type));
1806     enforceJSON(m_bigInt >= long.min && m_bigInt <= long.max,
1807         "Number out of range while converting BigInt(" ~ format("%d", m_bigInt) ~ ") to long.");
1808     return m_bigInt.toLong();
1809   }
1810 
1811   /*invariant()
1812 	{
1813 		assert(m_type >= Type.Undefined && m_type <= Type.Object);
1814 	}*/
1815 }
1816 
1817 @safe unittest { // issue #1234 - @safe toString
1818   auto j = JSON(true);
1819   j.toString((str) @safe {}, FormatSpec!char("s"));
1820   assert(j.toString() == "true");
1821 }
1822 
1823 /******************************************************************************/
1824 /* public functions                                                           */
1825 /******************************************************************************/
1826 
1827 /**
1828 	Parses the given range as a JSON string and returns the corresponding JSON object.
1829 
1830 	The range is shrunk during parsing, leaving any remaining text that is not part of
1831 	the JSON contents.
1832 
1833 	Throws a JSONException if any parsing error occured.
1834 */
1835 JSON parseJSON(R)(ref R range, int* line = null, string filename = null)
1836     if (is(R == string)) {
1837   JSON ret;
1838   enforceJSON(!range.empty, "JSON string is empty.", filename, 0);
1839 
1840   skipWhitespace(range, line);
1841 
1842   enforceJSON(!range.empty, "JSON string contains only whitespaces.", filename, 0);
1843 
1844   version (JSONLineNumbers) {
1845     int curline = line ? *line : 0;
1846   }
1847 
1848   bool minus = false;
1849   switch (range.front) {
1850   case 'f':
1851     enforceJSON(range[1 .. $].startsWith("alse"),
1852         "Expected 'false', got '" ~ range[0 .. min(5, $)] ~ "'.", filename, line);
1853     range.popFrontN(5);
1854     ret = false;
1855     break;
1856   case 'n':
1857     enforceJSON(range[1 .. $].startsWith("ull"),
1858         "Expected 'null', got '" ~ range[0 .. min(4, $)] ~ "'.", filename, line);
1859     range.popFrontN(4);
1860     ret = null;
1861     break;
1862   case 't':
1863     enforceJSON(range[1 .. $].startsWith("rue"),
1864         "Expected 'true', got '" ~ range[0 .. min(4, $)] ~ "'.", filename, line);
1865     range.popFrontN(4);
1866     ret = true;
1867     break;
1868 
1869   case '-':
1870   case '0': .. case '9':
1871     bool is_long_overflow;
1872     bool is_float;
1873     auto num = skipNumber(range, is_float, is_long_overflow);
1874     if (is_float) {
1875       ret = to!double(num);
1876     } else if (is_long_overflow) {
1877       ret = () @trusted { return BigInt(num.to!string); }();
1878     } else {
1879       ret = to!long(num);
1880     }
1881     break;
1882   case '\"':
1883     ret = skipJSONString(range);
1884     break;
1885   case '[':
1886     auto arr = appender!(JSON[]);
1887     range.popFront();
1888     while (true) {
1889       skipWhitespace(range, line);
1890       enforceJSON(!range.empty, "Missing ']' before EOF.", filename, line);
1891       if (range.front == ']')
1892         break;
1893       arr ~= parseJSON(range, line, filename);
1894       skipWhitespace(range, line);
1895       enforceJSON(!range.empty, "Missing ']' before EOF.", filename, line);
1896       enforceJSON(range.front == ',' || range.front == ']',
1897           format("Expected ']' or ',' - got '%s'.", range.front), filename, line);
1898       if (range.front == ']')
1899         break;
1900       else
1901         range.popFront();
1902     }
1903     range.popFront();
1904     ret = arr.data;
1905     break;
1906   case '{':
1907     JSON[string] obj;
1908     range.popFront();
1909     while (true) {
1910       skipWhitespace(range, line);
1911       enforceJSON(!range.empty, "Missing '}' before EOF.", filename, line);
1912       if (range.front == '}')
1913         break;
1914       string key = skipJSONString(range);
1915       skipWhitespace(range, line);
1916       enforceJSON(range.startsWith(":"), "Expected ':' for key '" ~ key ~ "'", filename, line);
1917       range.popFront();
1918       skipWhitespace(range, line);
1919       JSON itm = parseJSON(range, line, filename);
1920       obj[key] = itm;
1921       skipWhitespace(range, line);
1922       enforceJSON(!range.empty, "Missing '}' before EOF.", filename, line);
1923       enforceJSON(range.front == ',' || range.front == '}',
1924           format("Expected '}' or ',' - got '%s'.", range.front), filename, line);
1925       if (range.front == '}')
1926         break;
1927       else
1928         range.popFront();
1929     }
1930     range.popFront();
1931     ret = obj;
1932     break;
1933   default:
1934     enforceJSON(false, format("Expected valid JSON token, got '%s'.",
1935         range[0 .. min(12, $)]), filename, line);
1936     assert(false);
1937   }
1938 
1939   assert(ret.type != JSON.Type.undefined);
1940   version (JSONLineNumbers)
1941     ret.line = curline;
1942   return ret;
1943 }
1944 
1945 /**
1946 	Parses the given JSON string and returns the corresponding JSON object.
1947 
1948 	Throws a JSONException if any parsing error occurs.
1949 */
1950 JSON parseJSONString(string str, string filename = null) @safe {
1951   auto strcopy = str;
1952   int line = 0;
1953   auto ret = parseJSON(strcopy, () @trusted { return &line; }(), filename);
1954   enforceJSON(strcopy.strip().length == 0,
1955       "Expected end of string after JSON value.", filename, line);
1956   return ret;
1957 }
1958 
1959 @safe unittest {
1960   // These currently don't work at compile time
1961   assert(parseJSONString("17559991181826658461") == JSON(BigInt(17559991181826658461UL)));
1962   assert(parseJSONString("99999999999999999999999999") == () @trusted {
1963     return JSON(BigInt("99999999999999999999999999"));
1964   }());
1965   auto json = parseJSONString(`{"hey": "This is @à test éhééhhéhéé !%/??*&?\ud83d\udcec"}`);
1966   assert(json.toPrettyString() == parseJSONString(json.toPrettyString()).toPrettyString());
1967 
1968   bool test() {
1969     assert(parseJSONString("null") == JSON(null));
1970     assert(parseJSONString("true") == JSON(true));
1971     assert(parseJSONString("false") == JSON(false));
1972     assert(parseJSONString("1") == JSON(1));
1973     assert(parseJSONString("2.0") == JSON(2.0));
1974     assert(parseJSONString("\"test\"") == JSON("test"));
1975     assert(parseJSONString("[1, 2, 3]") == JSON([JSON(1), JSON(2), JSON(3)]));
1976     assert(parseJSONString("{\"a\": 1}") == JSON(["a": JSON(1)]));
1977     assert(parseJSONString(`"\\\/\b\f\n\r\t\u1234"`).get!string == "\\/\b\f\n\r\t\u1234");
1978 
1979     return true;
1980   }
1981 
1982   // Run at compile time and runtime
1983   assert(test());
1984   static assert(test());
1985 }
1986 
1987 @safe unittest {
1988   bool test() {
1989     try
1990       parseJSONString(" \t\n ");
1991     catch (Exception e)
1992       assert(e.msg.endsWith("JSON string contains only whitespaces."));
1993     try
1994       parseJSONString(`{"a": 1`);
1995     catch (Exception e)
1996       assert(e.msg.endsWith("Missing '}' before EOF."));
1997     try
1998       parseJSONString(`{"a": 1 x`);
1999     catch (Exception e)
2000       assert(e.msg.endsWith("Expected '}' or ',' - got 'x'."));
2001     try
2002       parseJSONString(`[1`);
2003     catch (Exception e)
2004       assert(e.msg.endsWith("Missing ']' before EOF."));
2005     try
2006       parseJSONString(`[1 x`);
2007     catch (Exception e)
2008       assert(e.msg.endsWith("Expected ']' or ',' - got 'x'."));
2009 
2010     return true;
2011   }
2012 
2013   // Run at compile time and runtime
2014   assert(test());
2015   static assert(test());
2016 }
2017 
2018 /**
2019 	Serializes the given value to JSON.
2020 
2021 	The following types of values are supported:
2022 
2023 	$(DL
2024 		$(DT `JSON`)            $(DD Used as-is)
2025 		$(DT `null`)            $(DD Converted to `JSON.Type.null_`)
2026 		$(DT `bool`)            $(DD Converted to `JSON.Type.bool_`)
2027 		$(DT `float`, `double`)   $(DD Converted to `JSON.Type.float_`)
2028 		$(DT `short`, `ushort`, `int`, `uint`, `long`, `ulong`) $(DD Converted to `JSON.Type.int_`)
2029 		$(DT `BigInt`)          $(DD Converted to `JSON.Type.bigInt`)
2030 		$(DT `string`)          $(DD Converted to `JSON.Type.string`)
2031 		$(DT `T[]`)             $(DD Converted to `JSON.Type.array`)
2032 		$(DT `T[string]`)       $(DD Converted to `JSON.Type.object`)
2033 		$(DT `struct`)          $(DD Converted to `JSON.Type.object`)
2034 		$(DT `class`)           $(DD Converted to `JSON.Type.object` or `JSON.Type.null_`)
2035 	)
2036 
2037 	All entries of an array or an associative array, as well as all R/W properties and
2038 	all public fields of a struct/class are recursively serialized using the same rules.
2039 
2040 	Fields ending with an underscore will have the last underscore stripped in the
2041 	serialized output. This makes it possible to use fields with D keywords as their name
2042 	by simply appending an underscore.
2043 
2044 	The following methods can be used to customize the serialization of structs/classes:
2045 
2046 	---
2047 	JSON toJSON() const;
2048 	static T fromJSON(JSON src);
2049 
2050 	string toString() const;
2051 	static T fromString(string src);
2052 	---
2053 
2054 	The methods will have to be defined in pairs. The first pair that is implemented by
2055 	the type will be used for serialization (i.e. `toJSON` overrides `toString`).
2056 
2057 	See_Also: `deserializeJSON`, `vibe.data.serialization`
2058 */
2059 JSON serializeToJSON(T)(auto ref T value) {
2060   return serialize!JSONSerializer(value);
2061 }
2062 /// ditto
2063 void serializeToJSON(R, T)(R destination, auto ref T value)
2064     if (isOutputRange!(R, char) || isOutputRange!(R, ubyte)) {
2065   serialize!(JSONStringSerializer!R)(value, destination);
2066 }
2067 /// ditto
2068 string serializeToJSONString(T)(auto ref T value) {
2069   auto ret = appender!string;
2070   serializeToJSON(ret, value);
2071   return ret.data;
2072 }
2073 
2074 ///
2075 @safe unittest {
2076   struct Foo {
2077     int number;
2078     string str;
2079   }
2080 
2081   Foo f;
2082 
2083   f.number = 12;
2084   f.str = "hello";
2085 
2086   string json = serializeToJSONString(f);
2087   assert(json == `{"number":12,"str":"hello"}`);
2088   JSON value = serializeToJSON(f);
2089   assert(value.type == JSON.Type.object);
2090   assert(value["number"] == JSON(12));
2091   assert(value["str"] == JSON("hello"));
2092 }
2093 
2094 /**
2095 	Serializes the given value to a pretty printed JSON string.
2096 
2097 	See_also: `serializeToJSON`, `vibe.data.serialization`
2098 */
2099 void serializeToPrettyJSON(R, T)(R destination, auto ref T value)
2100     if (isOutputRange!(R, char) || isOutputRange!(R, ubyte)) {
2101   serialize!(JSONStringSerializer!(R, true))(value, destination);
2102 }
2103 /// ditto
2104 string serializeToPrettyJSON(T)(auto ref T value) {
2105   auto ret = appender!string;
2106   serializeToPrettyJSON(ret, value);
2107   return ret.data;
2108 }
2109 
2110 ///
2111 @safe unittest {
2112   struct Foo {
2113     int number;
2114     string str;
2115   }
2116 
2117   Foo f;
2118   f.number = 12;
2119   f.str = "hello";
2120 
2121   string json = serializeToPrettyJSON(f);
2122   assert(json == `{
2123 	"number": 12,
2124 	"str": "hello"
2125 }`);
2126 }
2127 
2128 /**
2129 	Deserializes a JSON value into the destination variable.
2130 
2131 	The same types as for `serializeToJSON()` are supported and handled inversely.
2132 
2133 	See_Also: `serializeToJSON`, `serializeToJSONString`, `vibe.data.serialization`
2134 */
2135 void deserializeJSON(T)(ref T dst, JSON src) {
2136   dst = deserializeJSON!T(src);
2137 }
2138 /// ditto
2139 T deserializeJSON(T)(JSON src) {
2140   return deserialize!(JSONSerializer, T)(src);
2141 }
2142 /// ditto
2143 T deserializeJSON(T, R)(R input) if (!is(R == JSON) && isInputRange!R) {
2144   return deserialize!(JSONStringSerializer!R, T)(input);
2145 }
2146 
2147 ///
2148 @safe unittest {
2149   struct Foo {
2150     int number;
2151     string str;
2152   }
2153 
2154   Foo f = deserializeJSON!Foo(`{"number": 12, "str": "hello"}`);
2155   assert(f.number == 12);
2156   assert(f.str == "hello");
2157 }
2158 
2159 @safe unittest {
2160   import std.stdio;
2161 
2162   enum Foo : string {
2163     k = "test"
2164   }
2165 
2166   enum Boo : int {
2167     l = 5
2168   }
2169 
2170   static struct S {
2171     float a;
2172     double b;
2173     bool c;
2174     int d;
2175     string e;
2176     byte f;
2177     ubyte g;
2178     long h;
2179     ulong i;
2180     float[] j;
2181     Foo k;
2182     Boo l;
2183   }
2184 
2185   immutable S t = {
2186     1.5, -3.0, true, int.min, "Test", -128, 255, long.min, ulong.max, [
2187       1.1, 1.2, 1.3
2188     ], Foo.k, Boo.l};
2189     S u;
2190     deserializeJSON(u, serializeToJSON(t));
2191     assert(t.a == u.a);
2192     assert(t.b == u.b);
2193     assert(t.c == u.c);
2194     assert(t.d == u.d);
2195     assert(t.e == u.e);
2196     assert(t.f == u.f);
2197     assert(t.g == u.g);
2198     assert(t.h == u.h);
2199     assert(t.i == u.i);
2200     assert(t.j == u.j);
2201     assert(t.k == u.k);
2202     assert(t.l == u.l);
2203   }
2204 
2205   @safe unittest {
2206     assert(uint.max == serializeToJSON(uint.max).deserializeJSON!uint);
2207     assert(ulong.max == serializeToJSON(ulong.max).deserializeJSON!ulong);
2208   }
2209 
2210   unittest {
2211     static struct A {
2212       int value;
2213       static A fromJSON(JSON val) @safe {
2214         return A(val.get!int);
2215       }
2216 
2217       JSON toJSON() const @safe {
2218         return JSON(value);
2219       }
2220     }
2221 
2222     static struct C {
2223       int value;
2224       static C fromString(string val) @safe {
2225         return C(val.to!int);
2226       }
2227 
2228       string toString() const @safe {
2229         return value.to!string;
2230       }
2231     }
2232 
2233     static struct D {
2234       int value;
2235     }
2236 
2237     assert(serializeToJSON(const A(123)) == JSON(123));
2238     assert(serializeToJSON(A(123)) == JSON(123));
2239     assert(serializeToJSON(const C(123)) == JSON("123"));
2240     assert(serializeToJSON(C(123)) == JSON("123"));
2241     assert(serializeToJSON(const D(123)) == serializeToJSON(["value": 123]));
2242     assert(serializeToJSON(D(123)) == serializeToJSON(["value": 123]));
2243   }
2244 
2245   unittest {
2246     auto d = Date(2001, 1, 1);
2247     deserializeJSON(d, serializeToJSON(Date.init));
2248     assert(d == Date.init);
2249     deserializeJSON(d, serializeToJSON(Date(2001, 1, 1)));
2250     assert(d == Date(2001, 1, 1));
2251     struct S {
2252       immutable(int)[] x;
2253     }
2254 
2255     S s;
2256     deserializeJSON(s, serializeToJSON(S([1, 2, 3])));
2257     assert(s == S([1, 2, 3]));
2258     struct T {
2259       @optional S s;
2260       @optional int i;
2261       @optional float f_; // underscore strip feature
2262       @optional double d;
2263       @optional string str;
2264     }
2265 
2266     auto t = T(S([1, 2, 3]));
2267     deserializeJSON(t,
2268         parseJSONString(`{ "s" : null, "i" : null, "f" : null, "d" : null, "str" : null }`));
2269     assert(text(t) == text(T()));
2270   }
2271 
2272   unittest {
2273     static class C {
2274     @safe:
2275       int a;
2276       private int _b;
2277       @property int b() const {
2278         return _b;
2279       }
2280 
2281       @property void b(int v) {
2282         _b = v;
2283       }
2284 
2285       @property int test() const {
2286         return 10;
2287       }
2288 
2289       void test2() {
2290       }
2291     }
2292 
2293     C c = new C;
2294     c.a = 1;
2295     c.b = 2;
2296 
2297     C d;
2298     deserializeJSON(d, serializeToJSON(c));
2299     assert(c.a == d.a);
2300     assert(c.b == d.b);
2301   }
2302 
2303   unittest {
2304     static struct C {
2305     @safe:
2306       int value;
2307       static C fromString(string val) {
2308         return C(val.to!int);
2309       }
2310 
2311       string toString() const {
2312         return value.to!string;
2313       }
2314     }
2315 
2316     enum Color {
2317       Red,
2318       Green,
2319       Blue
2320     }
2321 
2322     {
2323       static class T {
2324       @safe:
2325         string[Color] enumIndexedMap;
2326         string[C] stringableIndexedMap;
2327         this() {
2328           enumIndexedMap = [Color.Red: "magenta", Color.Blue: "deep blue"];
2329           stringableIndexedMap = [C(42): "forty-two"];
2330         }
2331       }
2332 
2333       T original = new T;
2334       original.enumIndexedMap[Color.Green] = "olive";
2335       T other;
2336       deserializeJSON(other, serializeToJSON(original));
2337       assert(serializeToJSON(other) == serializeToJSON(original));
2338     }
2339     {
2340       static struct S {
2341         string[Color] enumIndexedMap;
2342         string[C] stringableIndexedMap;
2343       }
2344 
2345       S* original = new S;
2346       original.enumIndexedMap = [Color.Red: "magenta", Color.Blue: "deep blue"];
2347       original.enumIndexedMap[Color.Green] = "olive";
2348       original.stringableIndexedMap = [C(42): "forty-two"];
2349       S other;
2350       deserializeJSON(other, serializeToJSON(original));
2351       assert(serializeToJSON(other) == serializeToJSON(original));
2352     }
2353   }
2354 
2355   unittest {
2356     import std.typecons : Nullable;
2357 
2358     struct S {
2359       Nullable!int a, b;
2360     }
2361 
2362     S s;
2363     s.a = 2;
2364 
2365     auto j = serializeToJSON(s);
2366     assert(j["a"].type == JSON.Type.int_);
2367     assert(j["b"].type == JSON.Type.null_);
2368 
2369     auto t = deserializeJSON!S(j);
2370     assert(!t.a.isNull() && t.a == 2);
2371     assert(t.b.isNull());
2372   }
2373 
2374   unittest { // #840
2375     int[2][2] nestedArray = 1;
2376     assert(nestedArray.serializeToJSON.deserializeJSON!(typeof(nestedArray)) == nestedArray);
2377   }
2378 
2379   unittest { // #1109
2380     static class C {
2381     @safe:
2382       int mem;
2383       this(int m) {
2384         mem = m;
2385       }
2386 
2387       static C fromJSON(JSON j) {
2388         return new C(j.get!int - 1);
2389       }
2390 
2391       JSON toJSON() const {
2392         return JSON(mem + 1);
2393       }
2394     }
2395 
2396     const c = new C(13);
2397     assert(serializeToJSON(c) == JSON(14));
2398     assert(deserializeJSON!C(JSON(14)).mem == 13);
2399   }
2400 
2401   unittest { // const and mutable JSON
2402     JSON j = JSON(1);
2403     const k = JSON(2);
2404     assert(serializeToJSON(j) == JSON(1));
2405     assert(serializeToJSON(k) == JSON(2));
2406   }
2407 
2408   unittest { // issue #1660 - deserialize AA whose key type is string-based enum
2409     enum Foo : string {
2410       Bar = "bar",
2411       Buzz = "buzz"
2412     }
2413 
2414     struct S {
2415       int[Foo] f;
2416     }
2417 
2418     const s = S([Foo.Bar: 2000]);
2419     assert(serializeToJSON(s)["f"] == JSON([Foo.Bar: JSON(2000)]));
2420 
2421     auto j = JSON.emptyObject;
2422     j["f"] = [Foo.Bar: JSON(2000)];
2423     assert(deserializeJSON!S(j).f == [Foo.Bar: 2000]);
2424   }
2425 
2426   unittest {
2427     struct V {
2428       UUID v;
2429     }
2430 
2431     const u = UUID("318d7a61-e41b-494e-90d3-0a99f5531bfe");
2432     const s = `{"v":"318d7a61-e41b-494e-90d3-0a99f5531bfe"}`;
2433     auto j = JSON(["v": JSON(u)]);
2434 
2435     const v = V(u);
2436 
2437     assert(serializeToJSON(v) == j);
2438 
2439     j = JSON.emptyObject;
2440     j["v"] = u;
2441     assert(deserializeJSON!V(j).v == u);
2442 
2443     assert(serializeToJSONString(v) == s);
2444     assert(deserializeJSON!V(s).v == u);
2445   }
2446 
2447   /**
2448 	Serializer for a plain JSON representation.
2449 
2450 	See_Also: vibe.data.serialization.serialize, vibe.data.serialization.deserialize, serializeToJSON, deserializeJSON
2451 */
2452   struct JSONSerializer {
2453     template isJSONBasicType(T) {
2454       enum isJSONBasicType = std.traits.isNumeric!T || isBoolean!T
2455         || isSomeString!T || is(T == typeof(null)) || is(Unqual!T == UUID) || isJSONSerializable!T;
2456     }
2457 
2458     template isSupportedValueType(T) {
2459       enum isSupportedValueType = isJSONBasicType!T || is(Unqual!T == JSON)
2460         || is(Unqual!T == JSONValue);
2461     }
2462 
2463     private {
2464       JSON m_current;
2465       JSON[] m_compositeStack;
2466     }
2467 
2468     this(JSON data) @safe {
2469       m_current = data;
2470     }
2471 
2472     @disable this(this);
2473 
2474     //
2475     // serialization
2476     //
2477     JSON getSerializedResult() @safe {
2478       return m_current;
2479     }
2480 
2481     void beginWriteDictionary(Traits)() {
2482       m_compositeStack ~= JSON.emptyObject;
2483     }
2484 
2485     void endWriteDictionary(Traits)() {
2486       m_current = m_compositeStack[$ - 1];
2487       m_compositeStack.length--;
2488     }
2489 
2490     void beginWriteDictionaryEntry(Traits)(string name) {
2491     }
2492 
2493     void endWriteDictionaryEntry(Traits)(string name) {
2494       m_compositeStack[$ - 1][name] = m_current;
2495     }
2496 
2497     void beginWriteArray(Traits)(size_t) {
2498       m_compositeStack ~= JSON.emptyArray;
2499     }
2500 
2501     void endWriteArray(Traits)() {
2502       m_current = m_compositeStack[$ - 1];
2503       m_compositeStack.length--;
2504     }
2505 
2506     void beginWriteArrayEntry(Traits)(size_t) {
2507     }
2508 
2509     void endWriteArrayEntry(Traits)(size_t) {
2510       m_compositeStack[$ - 1].appendArrayElement(m_current);
2511     }
2512 
2513     void writeValue(Traits, T)(auto ref T value) if (!is(Unqual!T == JSON)) {
2514       alias UT = Unqual!T;
2515       static if (is(UT == JSONValue)) {
2516         m_current = JSON(value);
2517       } else static if (isJSONSerializable!UT) {
2518         static if (!__traits(compiles, ()@safe { return value.toJSON(); }()))
2519           pragma(msg,
2520               "Non-@safe toJSON/fromJSON methods are deprecated - annotate "
2521               ~ UT.stringof ~ ".toJSON() with @safe.");
2522         m_current = () @trusted { return value.toJSON(); }();
2523       } else static if (isSomeString!T && !is(UT == string)) {
2524         writeValue!Traits(value.to!string);
2525       } else
2526         m_current = JSON(value);
2527     }
2528 
2529     void writeValue(Traits, T)(auto ref T value) if (is(T == JSON)) {
2530       m_current = value;
2531     }
2532 
2533     void writeValue(Traits, T)(auto ref T value)
2534         if (!is(T == JSON) && is(T : const(JSON))) {
2535       m_current = value.clone;
2536     }
2537 
2538     //
2539     // deserialization
2540     //
2541     void readDictionary(Traits)(scope void delegate(string) @safe field_handler) {
2542       enforceJSON(m_current.type == JSON.Type.object,
2543           "Expected JSON object, got " ~ m_current.type.to!string);
2544       auto old = m_current;
2545       foreach (string key, value; m_current.get!(JSON[string])) {
2546         if (value.type == JSON.Type.undefined) {
2547           continue;
2548         }
2549 
2550         m_current = value;
2551         field_handler(key);
2552       }
2553       m_current = old;
2554     }
2555 
2556     void beginReadDictionaryEntry(Traits)(string name) {
2557     }
2558 
2559     void endReadDictionaryEntry(Traits)(string name) {
2560     }
2561 
2562     void readArray(Traits)(scope void delegate(size_t) @safe size_callback,
2563         scope void delegate() @safe entry_callback) {
2564       enforceJSON(m_current.type == JSON.Type.array,
2565           "Expected JSON array, got " ~ m_current.type.to!string);
2566       auto old = m_current;
2567       size_callback(m_current.length);
2568       foreach (ent; old.get!(JSON[])) {
2569         m_current = ent;
2570         entry_callback();
2571       }
2572       m_current = old;
2573     }
2574 
2575     void beginReadArrayEntry(Traits)(size_t index) {
2576     }
2577 
2578     void endReadArrayEntry(Traits)(size_t index) {
2579     }
2580 
2581     T readValue(Traits, T)() @safe {
2582       static if (is(T == JSON))
2583         return m_current;
2584       else static if (is(T == JSONValue))
2585         return cast(JSONValue) m_current;
2586       else static if (isJSONSerializable!T) {
2587         static if (!__traits(compiles, ()@safe { return T.fromJSON(m_current); }()))
2588           pragma(msg,
2589               "Non-@safe toJSON/fromJSON methods are deprecated - annotate "
2590               ~ T.stringof ~ ".fromJSON() with @safe.");
2591         return () @trusted { return T.fromJSON(m_current); }();
2592       } else static if (is(T == float) || is(T == double)) {
2593         switch (m_current.type) {
2594         default:
2595           return cast(T) m_current.get!long;
2596         case JSON.Type.null_:
2597           goto case;
2598         case JSON.Type.undefined:
2599           return T.nan;
2600         case JSON.Type.float_:
2601           return cast(T) m_current.get!double;
2602         case JSON.Type.bigInt:
2603           return cast(T) m_current.bigIntToLong();
2604         }
2605       } else static if (is(T == const(char)[])) {
2606         return readValue!(Traits, string);
2607       } else static if (isSomeString!T && !is(T == string)) {
2608         return readValue!(Traits, string).to!T;
2609       } else static if (is(T == string)) {
2610         if (m_current.type == JSON.Type.array) { // legacy support for pre-#2150 serialization results
2611           return () @trusted { // appender
2612             auto r = appender!string;
2613             foreach (s; m_current.get!(JSON[]))
2614               r.put(s.get!string());
2615             return r.data;
2616           }();
2617         } else
2618           return m_current.get!T();
2619       } else
2620         return m_current.get!T();
2621     }
2622 
2623     bool tryReadNull(Traits)() {
2624       return m_current.type == JSON.Type.null_;
2625     }
2626   }
2627 
2628   unittest {
2629     struct T {
2630       @optional string a;
2631     }
2632 
2633     auto obj = JSON.emptyObject;
2634     obj["a"] = JSON.undefined;
2635     assert(obj.deserializeJSON!T.a == "");
2636   }
2637 
2638   unittest {
2639     class C {
2640       this(JSON j) {
2641         foo = j;
2642       }
2643 
2644       JSON foo;
2645     }
2646 
2647     const C c = new C(JSON(42));
2648     assert(serializeToJSON(c)["foo"].get!int == 42);
2649   }
2650 
2651   /**
2652 	Serializer for a range based plain JSON string representation.
2653 
2654 	See_Also: vibe.data.serialization.serialize, vibe.data.serialization.deserialize, serializeToJSON, deserializeJSON
2655 */
2656   struct JSONStringSerializer(R, bool pretty = false)
2657       if (isInputRange!R || isOutputRange!(R, char)) {
2658     private {
2659       R m_range;
2660       size_t m_level = 0;
2661     }
2662 
2663     template isJSONBasicType(T) {
2664       enum isJSONBasicType = std.traits.isNumeric!T || isBoolean!T
2665         || isSomeString!T || is(T == typeof(null)) || is(Unqual!T == UUID) || isJSONSerializable!T;
2666     }
2667 
2668     template isSupportedValueType(T) {
2669       enum isSupportedValueType = isJSONBasicType!(Unqual!T)
2670         || is(Unqual!T == JSON) || is(Unqual!T == JSONValue);
2671     }
2672 
2673     this(R range) {
2674       m_range = range;
2675     }
2676 
2677     @disable this(this);
2678 
2679     //
2680     // serialization
2681     //
2682     static if (isOutputRange!(R, char)) {
2683       private {
2684         bool m_firstInComposite;
2685       }
2686 
2687       void getSerializedResult() {
2688       }
2689 
2690       void beginWriteDictionary(Traits)() {
2691         startComposite();
2692         m_range.put('{');
2693       }
2694 
2695       void endWriteDictionary(Traits)() {
2696         endComposite();
2697         m_range.put("}");
2698       }
2699 
2700       void beginWriteDictionaryEntry(Traits)(string name) {
2701         startCompositeEntry();
2702         m_range.put('"');
2703         m_range.JSONEscape(name);
2704         static if (pretty)
2705           m_range.put(`": `);
2706         else
2707           m_range.put(`":`);
2708       }
2709 
2710       void endWriteDictionaryEntry(Traits)(string name) {
2711       }
2712 
2713       void beginWriteArray(Traits)(size_t) {
2714         startComposite();
2715         m_range.put('[');
2716       }
2717 
2718       void endWriteArray(Traits)() {
2719         endComposite();
2720         m_range.put(']');
2721       }
2722 
2723       void beginWriteArrayEntry(Traits)(size_t) {
2724         startCompositeEntry();
2725       }
2726 
2727       void endWriteArrayEntry(Traits)(size_t) {
2728       }
2729 
2730       void writeValue(Traits, T)(in T value) {
2731         alias UT = Unqual!T;
2732         static if (is(T == typeof(null)))
2733           m_range.put("null");
2734         else static if (is(UT == bool))
2735           m_range.put(value ? "true" : "false");
2736         else static if (is(UT : long))
2737           m_range.formattedWrite("%s", value);
2738         else static if (is(UT == BigInt))
2739           () @trusted { m_range.formattedWrite("%d", value); }();
2740         else static if (is(UT : real))
2741           value == value ? m_range.formattedWrite("%.16g", value) : m_range.put("null");
2742         else static if (is(UT : const(char)[])) {
2743           m_range.put('"');
2744           m_range.JSONEscape(value);
2745           m_range.put('"');
2746         } else static if (isSomeString!T)
2747           writeValue!Traits(value.to!string); // TODO: avoid memory allocation
2748         else static if (is(UT == UUID))
2749           writeValue!Traits(value.toString());
2750         else static if (is(UT == JSON))
2751           m_range.writeJSONString(value);
2752         else static if (is(UT == JSONValue))
2753           m_range.writeJSONString(JSON(value));
2754         else static if (isJSONSerializable!UT) {
2755           static if (!__traits(compiles, ()@safe { return value.toJSON(); }()))
2756             pragma(msg,
2757                 "Non-@safe toJSON/fromJSON methods are deprecated - annotate "
2758                 ~ UT.stringof ~ ".toJSON() with @safe.");
2759           m_range.writeJSONString!(R, pretty)(() @trusted {
2760             return value.toJSON();
2761           }(), m_level);
2762         } else
2763           static assert(false, "Unsupported type: " ~ UT.stringof);
2764       }
2765 
2766       private void startComposite() {
2767         static if (pretty)
2768           m_level++;
2769         m_firstInComposite = true;
2770       }
2771 
2772       private void startCompositeEntry() {
2773         if (!m_firstInComposite) {
2774           m_range.put(',');
2775         } else {
2776           m_firstInComposite = false;
2777         }
2778         static if (pretty)
2779           indent();
2780       }
2781 
2782       private void endComposite() {
2783         static if (pretty) {
2784           m_level--;
2785           if (!m_firstInComposite)
2786             indent();
2787         }
2788         m_firstInComposite = false;
2789       }
2790 
2791       private void indent() {
2792         m_range.put('\n');
2793         foreach (i; 0 .. m_level)
2794           m_range.put('\t');
2795       }
2796     }
2797 
2798     //
2799     // deserialization
2800     //
2801     static if (isInputRange!(R)) {
2802       private {
2803         int m_line = 0;
2804       }
2805 
2806       void readDictionary(Traits)(scope void delegate(string) @safe entry_callback) {
2807         m_range.skipWhitespace(&m_line);
2808         enforceJSON(!m_range.empty && m_range.front == '{', "Expecting object.");
2809         m_range.popFront();
2810         bool first = true;
2811         while (true) {
2812           m_range.skipWhitespace(&m_line);
2813           enforceJSON(!m_range.empty, "Missing '}'.");
2814           if (m_range.front == '}') {
2815             m_range.popFront();
2816             break;
2817           } else if (!first) {
2818             enforceJSON(m_range.front == ',',
2819                 "Expecting ',' or '}', not '" ~ m_range.front.to!string ~ "'.");
2820             m_range.popFront();
2821             m_range.skipWhitespace(&m_line);
2822           } else
2823             first = false;
2824 
2825           auto name = m_range.skipJSONString(&m_line);
2826 
2827           m_range.skipWhitespace(&m_line);
2828           enforceJSON(!m_range.empty && m_range.front == ':',
2829               "Expecting ':', not '" ~ m_range.front.to!string ~ "'.");
2830           m_range.popFront();
2831 
2832           entry_callback(name);
2833         }
2834       }
2835 
2836       void beginReadDictionaryEntry(Traits)(string name) {
2837       }
2838 
2839       void endReadDictionaryEntry(Traits)(string name) {
2840       }
2841 
2842       void readArray(Traits)(scope void delegate(size_t) @safe size_callback,
2843           scope void delegate() @safe entry_callback) {
2844         m_range.skipWhitespace(&m_line);
2845         enforceJSON(!m_range.empty && m_range.front == '[', "Expecting array.");
2846         m_range.popFront();
2847         bool first = true;
2848         while (true) {
2849           m_range.skipWhitespace(&m_line);
2850           enforceJSON(!m_range.empty, "Missing ']'.");
2851           if (m_range.front == ']') {
2852             m_range.popFront();
2853             break;
2854           } else if (!first) {
2855             enforceJSON(m_range.front == ',', "Expecting ',' or ']'.");
2856             m_range.popFront();
2857           } else
2858             first = false;
2859 
2860           entry_callback();
2861         }
2862       }
2863 
2864       void beginReadArrayEntry(Traits)(size_t index) {
2865       }
2866 
2867       void endReadArrayEntry(Traits)(size_t index) {
2868       }
2869 
2870       T readValue(Traits, T)() {
2871         m_range.skipWhitespace(&m_line);
2872         static if (is(T == typeof(null))) {
2873           enforceJSON(m_range.take(4).equal("null"), "Expecting 'null'.");
2874           return null;
2875         } else static if (is(T == bool)) {
2876           bool ret = m_range.front == 't';
2877           string expected = ret ? "true" : "false";
2878           foreach (ch; expected) {
2879             enforceJSON(m_range.front == ch, "Expecting 'true' or 'false'.");
2880             m_range.popFront();
2881           }
2882           return ret;
2883         } else static if (is(T : long)) {
2884           bool is_float;
2885           bool is_long_overflow;
2886           auto num = m_range.skipNumber(is_float, is_long_overflow);
2887           enforceJSON(!is_float, "Expecting integer number.");
2888           enforceJSON(!is_long_overflow, num.to!string ~ " is too big for long.");
2889           return to!T(num);
2890         } else static if (is(T : BigInt)) {
2891           bool is_float;
2892           bool is_long_overflow;
2893           auto num = m_range.skipNumber(is_float, is_long_overflow);
2894           enforceJSON(!is_float, "Expecting integer number.");
2895           return BigInt(num);
2896         } else static if (is(T : real)) {
2897           bool is_float;
2898           bool is_long_overflow;
2899           auto num = m_range.skipNumber(is_float, is_long_overflow);
2900           return to!T(num);
2901         } else static if (is(T == string) || is(T == const(char)[])) {
2902           if (!m_range.empty && m_range.front == '[') {
2903             return () @trusted { // appender
2904               auto ret = appender!string();
2905               readArray!Traits((sz) {}, () @trusted {
2906                 ret.put(m_range.skipJSONString(&m_line));
2907               });
2908               return ret.data;
2909             }();
2910           } else
2911             return m_range.skipJSONString(&m_line);
2912         } else static if (isSomeString!T)
2913           return readValue!(Traits, string).to!T;
2914         else static if (is(T == UUID))
2915           return UUID(readValue!(Traits, string)());
2916         else static if (is(T == JSON))
2917           return m_range.parseJSON(&m_line);
2918         else static if (is(T == JSONValue))
2919           return cast(JSONValue) m_range.parseJSON(&m_line);
2920         else static if (isJSONSerializable!T) {
2921           static if (!__traits(compiles, ()@safe { return T.fromJSON(JSON.init); }()))
2922             pragma(msg,
2923                 "Non-@safe toJSON/fromJSON methods are deprecated - annotate "
2924                 ~ T.stringof ~ ".fromJSON() with @safe.");
2925           return () @trusted { return T.fromJSON(m_range.parseJSON(&m_line)); }();
2926         } else
2927           static assert(false, "Unsupported type: " ~ T.stringof);
2928       }
2929 
2930       bool tryReadNull(Traits)() {
2931         m_range.skipWhitespace(&m_line);
2932         if (m_range.front != 'n')
2933           return false;
2934         foreach (ch; "null") {
2935           enforceJSON(m_range.front == ch, "Expecting 'null'.");
2936           m_range.popFront();
2937         }
2938         assert(m_range.empty || m_range.front != 'l');
2939         return true;
2940       }
2941     }
2942   }
2943 
2944   /// Cloning JSON arrays
2945   unittest {
2946     JSON value = JSON([JSON([JSON.emptyArray]), JSON.emptyArray]).clone;
2947 
2948     assert(value.length == 2);
2949     assert(value[0].length == 1);
2950     assert(value[0][0].length == 0);
2951   }
2952 
2953   unittest {
2954     assert(serializeToJSONString(double.nan) == "null");
2955     assert(serializeToJSONString(JSON()) == "null");
2956     assert(serializeToJSONString(JSON(["bar": JSON("baz"), "foo": JSON()])) == `{"bar":"baz"}`);
2957 
2958     struct Foo {
2959       JSON bar = JSON();
2960     }
2961 
2962     Foo f;
2963     assert(serializeToJSONString(f) == `{"bar":null}`);
2964   }
2965 
2966   /**
2967 	Writes the given JSON object as a JSON string into the destination range.
2968 
2969 	This function will convert the given JSON value to a string without adding
2970 	any white space between tokens (no newlines, no indentation and no padding).
2971 	The output size is thus minimized, at the cost of bad human readability.
2972 
2973 	Params:
2974 		dst   = References the string output range to which the result is written.
2975 		JSON  = Specifies the JSON value that is to be stringified.
2976 		level = Specifies the base amount of indentation for the output. Indentation is always
2977 				done using tab characters.
2978 
2979 	See_Also: JSON.toString, writePrettyJSONString
2980 */
2981   void writeJSONString(R, bool pretty = false)(ref R dst, in JSON json, size_t level = 0) @safe //	if( isOutputRange!R && is(ElementEncodingType!R == char) )
2982   {
2983     final switch (json.type) {
2984     case JSON.Type.undefined:
2985       dst.put("null");
2986       break;
2987     case JSON.Type.null_:
2988       dst.put("null");
2989       break;
2990     case JSON.Type.bool_:
2991       dst.put(json.get!bool ? "true" : "false");
2992       break;
2993     case JSON.Type.int_:
2994       formattedWrite(dst, "%d", json.get!long);
2995       break;
2996     case JSON.Type.bigInt:
2997       () @trusted { formattedWrite(dst, "%d", json.get!BigInt); }();
2998       break;
2999     case JSON.Type.float_:
3000       auto d = json.get!double;
3001       if (d != d)
3002         dst.put("null"); // JSON has no NaN value so set null
3003       else
3004         formattedWrite(dst, "%.16g", json.get!double);
3005       break;
3006     case JSON.Type..string:
3007       dst.put('\"');
3008       JSONEscape(dst, json.get!string);
3009       dst.put('\"');
3010       break;
3011     case JSON.Type.array:
3012       dst.put('[');
3013       bool first = true;
3014       foreach (ref const JSON e; json.byValue) {
3015         if (!first)
3016           dst.put(",");
3017         first = false;
3018         static if (pretty) {
3019           dst.put('\n');
3020           foreach (tab; 0 .. level + 1)
3021             dst.put('\t');
3022         }
3023         if (e.type == JSON.Type.undefined)
3024           dst.put("null");
3025         else
3026           writeJSONString!(R, pretty)(dst, e, level + 1);
3027       }
3028       static if (pretty) {
3029         if (json.length > 0) {
3030           dst.put('\n');
3031           foreach (tab; 0 .. level)
3032             dst.put('\t');
3033         }
3034       }
3035       dst.put(']');
3036       break;
3037     case JSON.Type.object:
3038       dst.put('{');
3039       bool first = true;
3040       foreach (string k, ref const JSON e; json.byKeyValue) {
3041         if (e.type == JSON.Type.undefined)
3042           continue;
3043         if (!first)
3044           dst.put(',');
3045         first = false;
3046         static if (pretty) {
3047           dst.put('\n');
3048           foreach (tab; 0 .. level + 1)
3049             dst.put('\t');
3050         }
3051         dst.put('\"');
3052         JSONEscape(dst, k);
3053         dst.put(pretty ? `": ` : `":`);
3054         writeJSONString!(R, pretty)(dst, e, level + 1);
3055       }
3056       static if (pretty) {
3057         if (json.length > 0) {
3058           dst.put('\n');
3059           foreach (tab; 0 .. level)
3060             dst.put('\t');
3061         }
3062       }
3063       dst.put('}');
3064       break;
3065     }
3066   }
3067 
3068   unittest {
3069     auto a = JSON.emptyObject;
3070     a["a"] = JSON.emptyArray;
3071     a["b"] = JSON.emptyArray;
3072     a["b"] ~= JSON(1);
3073     a["b"] ~= JSON.emptyObject;
3074 
3075     assert(a.toString() == `{"a":[],"b":[1,{}]}` || a.toString() == `{"b":[1,{}],"a":[]}`);
3076     assert(a.toPrettyString() == `{
3077 	"a": [],
3078 	"b": [
3079 		1,
3080 		{}
3081 	]
3082 }` || a.toPrettyString() == `{
3083 	"b": [
3084 		1,
3085 		{}
3086 	],
3087 	"a": []
3088 }`);
3089   }
3090 
3091   unittest { // #735
3092     auto a = JSON.emptyArray;
3093     a ~= "a";
3094     a ~= JSON();
3095     a ~= "b";
3096     a ~= null;
3097     a ~= "c";
3098     assert(a.toString() == `["a",null,"b",null,"c"]`);
3099   }
3100 
3101   unittest {
3102     auto a = JSON.emptyArray;
3103     a ~= JSON(1);
3104     a ~= JSON(2);
3105     a ~= JSON(3);
3106     a ~= JSON(4);
3107     a ~= JSON(5);
3108 
3109     auto b = JSON(a[0 .. a.length]);
3110     assert(a == b);
3111 
3112     auto c = JSON(a[0 .. $]);
3113     assert(a == c);
3114     assert(b == c);
3115 
3116     auto d = [JSON(1), JSON(2), JSON(3)];
3117     assert(d == a[0 .. a.length - 2]);
3118     assert(d == a[0 .. $ - 2]);
3119   }
3120 
3121   unittest {
3122     auto j = JSON(double.init);
3123 
3124     assert(j.toString == "null"); // A double nan should serialize to null
3125     j = 17.04f;
3126     assert(j.toString == "17.04"); // A proper double should serialize correctly
3127 
3128     double d;
3129     deserializeJSON(d, JSON.undefined); // JSON.undefined should deserialize to nan
3130     assert(d != d);
3131     deserializeJSON(d, JSON(null)); // JSON.undefined should deserialize to nan
3132     assert(d != d);
3133   }
3134   /**
3135 	Writes the given JSON object as a prettified JSON string into the destination range.
3136 
3137 	The output will contain newlines and indents to make the output human readable.
3138 
3139 	Params:
3140 		dst   = References the string output range to which the result is written.
3141 		JSON  = Specifies the JSON value that is to be stringified.
3142 		level = Specifies the base amount of indentation for the output. Indentation  is always
3143 				done using tab characters.
3144 
3145 	See_Also: JSON.toPrettyString, writeJSONString
3146 */
3147   void writePrettyJSONString(R)(ref R dst, in JSON json, int level = 0) //	if( isOutputRange!R && is(ElementEncodingType!R == char) )
3148   {
3149     writeJSONString!(R, true)(dst, json, level);
3150   }
3151 
3152   /**
3153 	Helper function that escapes all Unicode characters in a JSON string.
3154 */
3155   string convertJSONToASCII(string json) {
3156     auto ret = appender!string;
3157     JSONEscape!true(ret, json);
3158     return ret.data;
3159   }
3160 
3161   /// private
3162   private void JSONEscape(bool escape_unicode = false, R)(ref R dst, const(char)[] s) {
3163     size_t startPos = 0;
3164 
3165     void putInterval(size_t curPos) {
3166       if (curPos > startPos)
3167         dst.put(s[startPos .. curPos]);
3168       startPos = curPos + 1;
3169     }
3170 
3171     for (size_t pos = 0; pos < s.length; pos++) {
3172       immutable(char) ch = s[pos];
3173 
3174       switch (ch) {
3175       default:
3176         static if (escape_unicode) {
3177           if (ch <= 0x20 || ch >= 0x80) {
3178             putInterval(pos);
3179             import std.utf : decode;
3180 
3181             int len;
3182             dchar codepoint = decode(s, pos);
3183             /* codepoint is in BMP */
3184             if (codepoint < 0x10000) {
3185               dst.formattedWrite("\\u%04X", codepoint);
3186             }  /* not in BMP -> construct a UTF-16 surrogate pair */
3187             else {
3188               int first, last;
3189 
3190               codepoint -= 0x10000;
3191               first = 0xD800 | ((codepoint & 0xffc00) >> 10);
3192               last = 0xDC00 | (codepoint & 0x003ff);
3193 
3194               dst.formattedWrite("\\u%04X\\u%04X", first, last);
3195             }
3196             startPos = pos;
3197             pos -= 1;
3198           }
3199         } else {
3200           if (ch < 0x20) {
3201             putInterval(pos);
3202             dst.formattedWrite("\\u%04X", ch);
3203           }
3204         }
3205         break;
3206       case '\\':
3207         putInterval(pos);
3208         dst.put("\\\\");
3209         break;
3210       case '\r':
3211         putInterval(pos);
3212         dst.put("\\r");
3213         break;
3214       case '\n':
3215         putInterval(pos);
3216         dst.put("\\n");
3217         break;
3218       case '\t':
3219         putInterval(pos);
3220         dst.put("\\t");
3221         break;
3222       case '\"':
3223         putInterval(pos);
3224         dst.put("\\\"");
3225         break;
3226       case '/':
3227         // this avoids the sequence "</" in the output, which is prone
3228         // to cross site scripting attacks when inserted into web pages
3229         if (pos > 0 && s[pos - 1] == '<') {
3230           putInterval(pos);
3231           dst.put("\\/");
3232         }
3233         break;
3234       }
3235     }
3236     // last interval
3237     putInterval(s.length);
3238   }
3239 
3240   /// private
3241   private string JSONUnescape(R)(ref R range) {
3242     auto ret = appender!string();
3243     while (!range.empty) {
3244       auto ch = range.front;
3245       switch (ch) {
3246       case '"':
3247         return ret.data;
3248       case '\\':
3249         range.popFront();
3250         enforceJSON(!range.empty, "Unterminated string escape sequence.");
3251         switch (range.front) {
3252         default:
3253           enforceJSON(false, "Invalid string escape sequence.");
3254           break;
3255         case '"':
3256           ret.put('\"');
3257           range.popFront();
3258           break;
3259         case '\\':
3260           ret.put('\\');
3261           range.popFront();
3262           break;
3263         case '/':
3264           ret.put('/');
3265           range.popFront();
3266           break;
3267         case 'b':
3268           ret.put('\b');
3269           range.popFront();
3270           break;
3271         case 'f':
3272           ret.put('\f');
3273           range.popFront();
3274           break;
3275         case 'n':
3276           ret.put('\n');
3277           range.popFront();
3278           break;
3279         case 'r':
3280           ret.put('\r');
3281           range.popFront();
3282           break;
3283         case 't':
3284           ret.put('\t');
3285           range.popFront();
3286           break;
3287         case 'u':
3288 
3289           dchar decode_unicode_escape() {
3290             enforceJSON(range.front == 'u');
3291             range.popFront();
3292             dchar uch = 0;
3293             foreach (i; 0 .. 4) {
3294               uch *= 16;
3295               enforceJSON(!range.empty, "Unicode sequence must be '\\uXXXX'.");
3296               auto dc = range.front;
3297               range.popFront();
3298 
3299               if (dc >= '0' && dc <= '9')
3300                 uch += dc - '0';
3301               else if (dc >= 'a' && dc <= 'f')
3302                 uch += dc - 'a' + 10;
3303               else if (dc >= 'A' && dc <= 'F')
3304                 uch += dc - 'A' + 10;
3305               else
3306                 enforceJSON(false, "Unicode sequence must be '\\uXXXX'.");
3307             }
3308             return uch;
3309           }
3310 
3311           auto uch = decode_unicode_escape();
3312 
3313           if (0xD800 <= uch && uch <= 0xDBFF) {
3314             /* surrogate pair */
3315             range.popFront(); // backslash '\'
3316             auto uch2 = decode_unicode_escape();
3317             enforceJSON(0xDC00 <= uch2 && uch2 <= 0xDFFF, "invalid Unicode");
3318             {
3319               /* valid second surrogate */
3320               uch = ((uch - 0xD800) << 10) + (uch2 - 0xDC00) + 0x10000;
3321             }
3322           }
3323           ret.put(uch);
3324           break;
3325         }
3326         break;
3327       default:
3328         ret.put(ch);
3329         range.popFront();
3330         break;
3331       }
3332     }
3333     return ret.data;
3334   }
3335 
3336   private auto skipNumber(R)(ref R s, out bool is_float, out bool is_long_overflow) @safe
3337       if (isNarrowString!R) {
3338     auto r = s.representation;
3339     version (assert) auto rEnd = (() @trusted => r.ptr + r.length - 1)();
3340     auto res = skipNumber(r, is_float, is_long_overflow);
3341     version (assert)
3342       assert(rEnd == (() @trusted => r.ptr + r.length - 1)()); // check nothing taken off the end
3343     s = s[$ - r.length .. $];
3344     return res.assumeUTF();
3345   }
3346 
3347   /// private
3348   private auto skipNumber(R)(ref R s, out bool is_float, out bool is_long_overflow)
3349       if (!isNarrowString!R && isForwardRange!R) {
3350     auto sOrig = s.save;
3351     size_t idx = 0;
3352     is_float = false;
3353     is_long_overflow = false;
3354     ulong int_part = 0;
3355     if (s.front == '-') {
3356       s.popFront();
3357       ++idx;
3358     }
3359     if (s.front == '0') {
3360       s.popFront();
3361       ++idx;
3362     } else {
3363       enforceJSON(isDigit(s.front), "Digit expected at beginning of number.");
3364       int_part = s.front - '0';
3365       s.popFront();
3366       ++idx;
3367       while (!s.empty && isDigit(s.front)) {
3368         if (!is_long_overflow) {
3369           auto dig = s.front - '0';
3370           if ((long.max / 10) > int_part || ((long.max / 10) == int_part && (long.max % 10) >= dig)) {
3371             int_part *= 10;
3372             int_part += dig;
3373           } else {
3374             is_long_overflow = true;
3375           }
3376         }
3377         s.popFront();
3378         ++idx;
3379       }
3380     }
3381 
3382     if (!s.empty && s.front == '.') {
3383       s.popFront();
3384       ++idx;
3385       is_float = true;
3386       while (!s.empty && isDigit(s.front)) {
3387         s.popFront();
3388         ++idx;
3389       }
3390     }
3391 
3392     if (!s.empty && (s.front == 'e' || s.front == 'E')) {
3393       s.popFront();
3394       ++idx;
3395       is_float = true;
3396       if (!s.empty && (s.front == '+' || s.front == '-')) {
3397         s.popFront();
3398         ++idx;
3399       }
3400       enforceJSON(!s.empty && isDigit(s.front),
3401           "Expected exponent." ~ sOrig.takeExactly(idx).to!string);
3402       s.popFront();
3403       ++idx;
3404       while (!s.empty && isDigit(s.front)) {
3405         s.popFront();
3406         ++idx;
3407       }
3408     }
3409 
3410     return sOrig.takeExactly(idx);
3411   }
3412 
3413   unittest {
3414     import std.meta : AliasSeq;
3415 
3416     // test for string and for a simple range
3417     foreach (foo; AliasSeq!(to!string, map!"a")) {
3418       auto test_1 = foo("9223372036854775806"); // lower then long.max
3419       auto test_2 = foo("9223372036854775807"); // long.max
3420       auto test_3 = foo("9223372036854775808"); // greater then long.max
3421       bool is_float;
3422       bool is_long_overflow;
3423       test_1.skipNumber(is_float, is_long_overflow);
3424       assert(!is_long_overflow);
3425       test_2.skipNumber(is_float, is_long_overflow);
3426       assert(!is_long_overflow);
3427       test_3.skipNumber(is_float, is_long_overflow);
3428       assert(is_long_overflow);
3429     }
3430   }
3431 
3432   /// private
3433   private string skipJSONString(R)(ref R s, int* line = null) {
3434     // TODO: count or disallow any newlines inside of the string
3435     enforceJSON(!s.empty && s.front == '"', "Expected '\"' to start string.");
3436     s.popFront();
3437     string ret = JSONUnescape(s);
3438     enforceJSON(!s.empty && s.front == '"', "Expected '\"' to terminate string.");
3439     s.popFront();
3440     return ret;
3441   }
3442 
3443   /// private
3444   private void skipWhitespace(R)(ref R s, int* line = null) {
3445     while (!s.empty) {
3446       switch (s.front) {
3447       default:
3448         return;
3449       case ' ', '\t':
3450         s.popFront();
3451         break;
3452       case '\n':
3453         s.popFront();
3454         if (!s.empty && s.front == '\r')
3455           s.popFront();
3456         if (line)
3457           (*line)++;
3458         break;
3459       case '\r':
3460         s.popFront();
3461         if (!s.empty && s.front == '\n')
3462           s.popFront();
3463         if (line)
3464           (*line)++;
3465         break;
3466       }
3467     }
3468   }
3469 
3470   private bool isDigit(dchar ch) @safe nothrow pure {
3471     return ch >= '0' && ch <= '9';
3472   }
3473 
3474   private string underscoreStrip(string field_name) @safe nothrow pure {
3475     if (field_name.length < 1 || field_name[$ - 1] != '_')
3476       return field_name;
3477     else
3478       return field_name[0 .. $ - 1];
3479   }
3480 
3481   /// private
3482   package template isJSONSerializable(T) {
3483     enum isJSONSerializable = is(typeof(T.init.toJSON()) : JSON)
3484       && is(typeof(T.fromJSON(JSON())) : T);
3485   }
3486 
3487   private void enforceJSON(string file = __FILE__, size_t line = __LINE__)(
3488       bool cond, lazy string message = "JSON exception") {
3489     import dutils.data.utils.exception : enforce;
3490 
3491     enforce!JSONException(cond, message, file, line);
3492   }
3493 
3494   private void enforceJSON(string file = __FILE__, size_t line = __LINE__)(
3495       bool cond, lazy string message, string err_file, int err_line) {
3496     import dutils.data.utils.exception : enforce;
3497 
3498     enforce!JSONException(cond, format("%s(%s): Error: %s", err_file,
3499         err_line + 1, message), file, line);
3500   }
3501 
3502   private void enforceJSON(string file = __FILE__, size_t line = __LINE__)(
3503       bool cond, lazy string message, string err_file, int* err_line) {
3504     enforceJSON!(file, line)(cond, message, err_file, err_line ? *err_line : -1);
3505   }
3506 
3507   private auto trustedRange(R)(R range) {
3508     static struct Rng {
3509       private R range;
3510       @property bool empty() @trusted {
3511         return range.empty;
3512       }
3513 
3514       @property auto front() @trusted {
3515         return range.front;
3516       }
3517 
3518       void popFront() @trusted {
3519         range.popFront();
3520       }
3521     }
3522 
3523     return Rng(range);
3524   }
3525 
3526   // make sure JSON is usable for CTFE
3527   @safe unittest {
3528     static assert(is(typeof({
3529           struct Test {
3530             JSON object_ = JSON.emptyObject;
3531             JSON array = JSON.emptyArray;
3532           }
3533         })), "CTFE for JSON type failed.");
3534 
3535     static JSON test() {
3536       JSON j;
3537       j = JSON(42);
3538       j = JSON([JSON(true)]);
3539       j = JSON(["foo": JSON(null)]);
3540       j = JSON("foo");
3541       return j;
3542     }
3543 
3544     enum j = test();
3545     static assert(j == JSON("foo"));
3546   }
3547 
3548   @safe unittest { // XSS prevention
3549     assert(JSON("</script>some/path").toString() == `"<\/script>some/path"`);
3550     assert(serializeToJSONString("</script>some/path") == `"<\/script>some/path"`);
3551   }
3552 
3553   @system unittest { // Recursive structures
3554     static struct Bar {
3555       Bar[] foos;
3556       int i;
3557     }
3558 
3559     auto b = deserializeJSON!Bar(`{"i":1,"foos":[{"foos":[],"i":2}]}`);
3560     assert(b.i == 1);
3561     assert(b.foos.length == 1);
3562     assert(b.foos[0].i == 2);
3563     assert(b.foos[0].foos.length == 0);
3564   }
3565 
3566   @safe unittest { // JSON <-> std.json.JSONValue
3567     auto astr = `{
3568 		"null": null,
3569 		"string": "Hello",
3570 		"integer": 123456,
3571 		"uinteger": 18446744073709551614,
3572 		"float": 12.34,
3573 		"object": { "hello": "world" },
3574 		"array": [1, 2, "string"],
3575 		"true": true,
3576 		"false": false
3577 	}`;
3578     auto a = parseJSONString(astr);
3579 
3580     // test JSONValue -> JSON conversion
3581     assert(JSON(cast(JSONValue) a) == a);
3582 
3583     // test JSON -> JSONValue conversion
3584     auto v = cast(JSONValue) a;
3585     assert(deserializeJSON!JSONValue(serializeToJSON(v)) == v);
3586 
3587     // test JSON strint <-> JSONValue serialization
3588     assert(deserializeJSON!JSONValue(astr) == v);
3589     assert(parseJSONString(serializeToJSONString(v)) == a);
3590 
3591     // test using std.conv for the conversion
3592     import std.conv : to;
3593 
3594     assert(a.to!JSONValue
3595         .to!JSON == a);
3596     assert(to!JSON(to!JSONValue(a)) == a);
3597   }
3598 
3599   @safe unittest { // issue #2150 - serialization of const/mutable strings + wide character strings
3600     assert(serializeToJSON(cast(const(char)[]) "foo") == JSON("foo"));
3601     assert(serializeToJSON("foo".dup) == JSON("foo"));
3602     assert(deserializeJSON!string(JSON("foo")) == "foo");
3603     assert(deserializeJSON!string(JSON([JSON("f"), JSON("o"), JSON("o")])) == "foo");
3604     assert(serializeToJSONString(cast(const(char)[]) "foo") == "\"foo\"");
3605     assert(deserializeJSON!string("\"foo\"") == "foo");
3606 
3607     assert(serializeToJSON(cast(const(wchar)[]) "foo"w) == JSON("foo"));
3608     assert(serializeToJSON("foo"w.dup) == JSON("foo"));
3609     assert(deserializeJSON!wstring(JSON("foo")) == "foo");
3610     assert(deserializeJSON!wstring(JSON([JSON("f"), JSON("o"), JSON("o")])) == "foo");
3611     assert(serializeToJSONString(cast(const(wchar)[]) "foo"w) == "\"foo\"");
3612     assert(deserializeJSON!wstring("\"foo\"") == "foo");
3613 
3614     assert(serializeToJSON(cast(const(dchar)[]) "foo"d) == JSON("foo"));
3615     assert(serializeToJSON("foo"d.dup) == JSON("foo"));
3616     assert(deserializeJSON!dstring(JSON("foo")) == "foo");
3617     assert(deserializeJSON!dstring(JSON([JSON("f"), JSON("o"), JSON("o")])) == "foo");
3618     assert(serializeToJSONString(cast(const(dchar)[]) "foo"d) == "\"foo\"");
3619     assert(deserializeJSON!dstring("\"foo\"") == "foo");
3620   }