1 /**
2 	JSON serialization and value handling.
3 
4 	This module provides the Json struct for reading, writing and manipulating
5 	JSON values. De(serialization) of arbitrary D types is also supported and
6 	is recommended for handling JSON in performance sensitive applications.
7 
8 	Copyright: © 2012-2015 RejectedSoftware e.K.
9 	License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file.
10 	Authors: Sönke Ludwig
11 */
12 module dub.internal.vibecompat.data.json;
13 
14 version (Have_vibe_d_data) public import vibe.data.json;
15 else:
16 
17 import dub.internal.vibecompat.data.utils;
18 
19 public import dub.internal.vibecompat.data.serialization;
20 
21 public import std.json : JSONException;
22 import std.algorithm : equal, min;
23 import std.array;
24 import std.conv;
25 import std.datetime;
26 import std.exception;
27 import std.format;
28 import std.range;
29 import std.string : format;
30 import std.traits;
31 
32 version = JsonLineNumbers;
33 version = VibeJsonFieldNames;
34 
35 
36 /******************************************************************************/
37 /* public types                                                               */
38 /******************************************************************************/
39 
40 /**
41 	Represents a single JSON value.
42 
43 	Json values can have one of the types defined in the Json.Type enum. They
44 	behave mostly like values in ECMA script in the way that you can
45 	transparently perform operations on them. However, strict typechecking is
46 	done, so that operations between differently typed JSON values will throw
47 	a JSONException. Additionally, an explicit cast or using get!() or to!() is
48 	required to convert a JSON value to the corresponding static D type.
49 */
50 struct Json {
51 	private {
52 		// putting all fields in a union results in many false pointers leading to
53 		// memory leaks and, worse, std.algorithm.swap triggering an assertion
54 		// because of internal pointers. This crude workaround seems to fix
55 		// the issues.
56 		void*[2] m_data;
57 		ref inout(T) getDataAs(T)() inout { static assert(T.sizeof <= m_data.sizeof); return *cast(inout(T)*)m_data.ptr; }
58 		@property ref inout(long) m_int() inout return { return getDataAs!long(); }
59 		@property ref inout(double) m_float() inout return { return getDataAs!double(); }
60 		@property ref inout(bool) m_bool() inout return { return getDataAs!bool(); }
61 		@property ref inout(string) m_string() inout return { return getDataAs!string(); }
62 		@property ref inout(Json[string]) m_object() inout return { return getDataAs!(Json[string])(); }
63 		@property ref inout(Json[]) m_array() inout return { return getDataAs!(Json[])(); }
64 
65 		Type m_type = Type.undefined;
66 
67 		version (VibeJsonFieldNames) {
68 			uint m_magic = 0x1337f00d; // works around Appender bug (DMD BUG 10690/10859/11357)
69 			string m_name;
70 			string m_fileName;
71 		}
72 	}
73 
74 	/** Represents the run time type of a JSON value.
75 	*/
76 	enum Type {
77 		undefined,  /// A non-existent value in a JSON object
78 		null_,      /// Null value
79 		bool_,      /// Boolean value
80 		int_,       /// 64-bit integer value
81 		float_,     /// 64-bit floating point value
82 		string,     /// UTF-8 string
83 		array,      /// Array of JSON values
84 		object,     /// JSON object aka. dictionary from string to Json
85 
86 		Undefined = undefined,  /// Compatibility alias - will be deprecated soon
87 		Null = null_,           /// Compatibility alias - will be deprecated soon
88 		Bool = bool_,           /// Compatibility alias - will be deprecated soon
89 		Int = int_,             /// Compatibility alias - will be deprecated soon
90 		Float = float_,         /// Compatibility alias - will be deprecated soon
91 		String = string,        /// Compatibility alias - will be deprecated soon
92 		Array = array,          /// Compatibility alias - will be deprecated soon
93 		Object = object         /// Compatibility alias - will be deprecated soon
94 	}
95 
96 	/// New JSON value of Type.Undefined
97 	static @property Json undefined() { return Json(); }
98 
99 	/// New JSON value of Type.Object
100 	static @property Json emptyObject() { return Json(cast(Json[string])null); }
101 
102 	/// New JSON value of Type.Array
103 	static @property Json emptyArray() { return Json(cast(Json[])null); }
104 
105 	version(JsonLineNumbers) int line;
106 
107 	/**
108 		Constructor for a JSON object.
109 	*/
110 	this(typeof(null)) { m_type = Type.null_; }
111 	/// ditto
112 	this(bool v) { m_type = Type.bool_; m_bool = v; }
113 	/// ditto
114 	this(byte v) { this(cast(long)v); }
115 	/// ditto
116 	this(ubyte v) { this(cast(long)v); }
117 	/// ditto
118 	this(short v) { this(cast(long)v); }
119 	/// ditto
120 	this(ushort v) { this(cast(long)v); }
121 	/// ditto
122 	this(int v) { this(cast(long)v); }
123 	/// ditto
124 	this(uint v) { this(cast(long)v); }
125 	/// ditto
126 	this(long v) { m_type = Type.int_; m_int = v; }
127 	/// ditto
128 	this(double v) { m_type = Type.float_; m_float = v; }
129 	/// ditto
130 	this(string v) { m_type = Type..string; m_string = v; }
131 	/// ditto
132 	this(Json[] v) { m_type = Type.array; m_array = v; }
133 	/// ditto
134 	this(Json[string] v) { m_type = Type.object; m_object = v; }
135 
136 	/**
137 		Allows assignment of D values to a JSON value.
138 	*/
139 	ref Json opAssign(Json v) return
140 	{
141 		m_type = v.m_type;
142 		final switch(m_type){
143 			case Type.undefined: m_string = null; break;
144 			case Type.null_: m_string = null; break;
145 			case Type.bool_: m_bool = v.m_bool; break;
146 			case Type.int_: m_int = v.m_int; break;
147 			case Type.float_: m_float = v.m_float; break;
148 			case Type..string: m_string = v.m_string; break;
149 			case Type.array: opAssign(v.m_array); break;
150 			case Type.object: opAssign(v.m_object); break;
151 		}
152 		return this;
153 	}
154 	/// ditto
155 	void opAssign(typeof(null)) { m_type = Type.null_; m_string = null; }
156 	/// ditto
157 	bool opAssign(bool v) { m_type = Type.bool_; m_bool = v; return v; }
158 	/// ditto
159 	int opAssign(int v) { m_type = Type.int_; m_int = v; return v; }
160 	/// ditto
161 	long opAssign(long v) { m_type = Type.int_; m_int = v; return v; }
162 	/// ditto
163 	double opAssign(double v) { m_type = Type.float_; m_float = v; return v; }
164 	/// ditto
165 	string opAssign(string v) { m_type = Type..string; m_string = v; return v; }
166 	/// ditto
167 	Json[] opAssign(Json[] v)
168 	{
169 		m_type = Type.array;
170 		m_array = v;
171 		version (VibeJsonFieldNames) { if (m_magic == 0x1337f00d) { foreach (idx, ref av; m_array) av.m_name = format("%s[%s]", m_name, idx); } else m_name = null; }
172 		return v;
173 	}
174 	/// ditto
175 	Json[string] opAssign(Json[string] v)
176 	{
177 		m_type = Type.object;
178 		m_object = v;
179 		version (VibeJsonFieldNames) { if (m_magic == 0x1337f00d) { foreach (key, ref av; m_object) av.m_name = format("%s.%s", m_name, key); } else m_name = null; }
180 		return v;
181 	}
182 
183 	/**
184 		Allows removal of values from Type.Object Json objects.
185 	*/
186 	void remove(string item) { checkType!(Json[string])(); m_object.remove(item); }
187 
188 	/**
189 		The current type id of this JSON object.
190 	*/
191 	@property Type type() const { return m_type; }
192 
193 	/**
194 		Clones a JSON value recursively.
195 	*/
196 	Json clone()
197 	const {
198 		final switch (m_type) {
199 			case Type.undefined: return Json.undefined;
200 			case Type.null_: return Json(null);
201 			case Type.bool_: return Json(m_bool);
202 			case Type.int_: return Json(m_int);
203 			case Type.float_: return Json(m_float);
204 			case Type..string: return Json(m_string);
205 			case Type.array:
206 				auto ret = Json.emptyArray;
207 				foreach (v; this) ret ~= v.clone();
208 				return ret;
209 			case Type.object:
210 				auto ret = Json.emptyObject;
211 				foreach (string name, v; this) ret[name] = v.clone();
212 				return ret;
213 		}
214 	}
215 
216 	/**
217 		Check whether the JSON object contains the given key and if yes,
218 		return a pointer to the corresponding object, otherwise return `null`.
219 	*/
220 	inout(Json*) opBinaryRight(string op : "in")(string key) inout {
221 		checkType!(Json[string])();
222 		return key in m_object;
223 	}
224 
225 	/**
226 		Allows direct indexing of array typed JSON values.
227 	*/
228 	ref inout(Json) opIndex(size_t idx) inout { checkType!(Json[])(); return m_array[idx]; }
229 
230 	///
231 	unittest {
232 		Json value = Json.emptyArray;
233 		value ~= 1;
234 		value ~= true;
235 		value ~= "foo";
236 		assert(value[0] == 1);
237 		assert(value[1] == true);
238 		assert(value[2] == "foo");
239 	}
240 
241 
242 	/**
243 		Allows direct indexing of object typed JSON values using a string as
244 		the key.
245 	*/
246 	const(Json) opIndex(string key)
247 	const {
248 		checkType!(Json[string])();
249 		if( auto pv = key in m_object ) return *pv;
250 		Json ret = Json.undefined;
251 		ret.m_string = key;
252 		version (VibeJsonFieldNames) ret.m_name = format("%s.%s", m_name, key);
253 		return ret;
254 	}
255 	/// ditto
256 	ref Json opIndex(string key)
257 	{
258 		checkType!(Json[string])();
259 		if( auto pv = key in m_object )
260 			return *pv;
261 		if (m_object is null) {
262 			m_object = ["": Json.init];
263 			m_object.remove("");
264 		}
265 		m_object[key] = Json.init;
266 		assert(m_object !is null);
267 		assert(key in m_object, "Failed to insert key '"~key~"' into AA!?");
268 		m_object[key].m_type = Type.undefined; // DMDBUG: AAs are the $H1T!!!11
269 		assert(m_object[key].type == Type.undefined);
270 		m_object[key].m_string = key;
271 		version (VibeJsonFieldNames) m_object[key].m_name = format("%s.%s", m_name, key);
272 		return m_object[key];
273 	}
274 
275 	///
276 	unittest {
277 		Json value = Json.emptyObject;
278 		value["a"] = 1;
279 		value["b"] = true;
280 		value["c"] = "foo";
281 		assert(value["a"] == 1);
282 		assert(value["b"] == true);
283 		assert(value["c"] == "foo");
284 	}
285 
286 	/**
287 		Returns a slice of a JSON array.
288 	*/
289 	inout(Json[]) opSlice() inout { checkType!(Json[])(); return m_array; }
290 	///
291 	inout(Json[]) opSlice(size_t from, size_t to) inout { checkType!(Json[])(); return m_array[from .. to]; }
292 
293 	/**
294 		Returns the number of entries of string, array or object typed JSON values.
295 	*/
296 	@property size_t length()
297 	const {
298 		checkType!(string, Json[], Json[string])("property length");
299 		switch(m_type){
300 			case Type..string: return m_string.length;
301 			case Type.array: return m_array.length;
302 			case Type.object: return m_object.length;
303 			default: assert(false);
304 		}
305 	}
306 
307 	/**
308 		Allows foreach iterating over JSON objects and arrays.
309 	*/
310 	int opApply(int delegate(ref Json obj) del)
311 	{
312 		checkType!(Json[], Json[string])("opApply");
313 		if( m_type == Type.array ){
314 			foreach( ref v; m_array )
315 				if( auto ret = del(v) )
316 					return ret;
317 			return 0;
318 		} else {
319 			foreach( ref v; m_object )
320 				if( v.type != Type.undefined )
321 					if( auto ret = del(v) )
322 						return ret;
323 			return 0;
324 		}
325 	}
326 	/// ditto
327 	int opApply(int delegate(ref const Json obj) del)
328 	const {
329 		checkType!(Json[], Json[string])("opApply");
330 		if( m_type == Type.array ){
331 			foreach( ref v; m_array )
332 				if( auto ret = del(v) )
333 					return ret;
334 			return 0;
335 		} else {
336 			foreach( ref v; m_object )
337 				if( v.type != Type.undefined )
338 					if( auto ret = del(v) )
339 						return ret;
340 			return 0;
341 		}
342 	}
343 	/// ditto
344 	int opApply(int delegate(ref size_t idx, ref Json obj) del)
345 	{
346 		checkType!(Json[])("opApply");
347 		foreach( idx, ref v; m_array )
348 			if( auto ret = del(idx, v) )
349 				return ret;
350 		return 0;
351 	}
352 	/// ditto
353 	int opApply(int delegate(ref size_t idx, ref const Json obj) del)
354 	const {
355 		checkType!(Json[])("opApply");
356 		foreach( idx, ref v; m_array )
357 			if( auto ret = del(idx, v) )
358 				return ret;
359 		return 0;
360 	}
361 	/// ditto
362 	int opApply(int delegate(ref string idx, ref Json obj) del)
363 	{
364 		checkType!(Json[string])("opApply");
365 		foreach( idx, ref v; m_object )
366 			if( v.type != Type.undefined )
367 				if( auto ret = del(idx, v) )
368 					return ret;
369 		return 0;
370 	}
371 	/// ditto
372 	int opApply(int delegate(ref string idx, ref const Json obj) del)
373 	const {
374 		checkType!(Json[string])("opApply");
375 		foreach( idx, ref v; m_object )
376 			if( v.type != Type.undefined )
377 				if( auto ret = del(idx, v) )
378 					return ret;
379 		return 0;
380 	}
381 
382 	/**
383 		Converts the JSON value to the corresponding D type - types must match exactly.
384 
385 		Available_Types:
386 			$(UL
387 				$(LI `bool` (`Type.bool_`))
388 				$(LI `double` (`Type.float_`))
389 				$(LI `float` (Converted from `double`))
390 				$(LI `long` (`Type.int_`))
391 				$(LI `ulong`, `int`, `uint`, `short`, `ushort`, `byte`, `ubyte` (Converted from `long`))
392 				$(LI `string` (`Type.string`))
393 				$(LI `Json[]` (`Type.array`))
394 				$(LI `Json[string]` (`Type.object`))
395 			)
396 
397 		See_Also: `opt`, `to`, `deserializeJson`
398 	*/
399 	inout(T) opCast(T)() inout { return get!T; }
400 	/// ditto
401 	@property inout(T) get(T)()
402 	inout {
403 		checkType!T();
404 		static if (is(T == bool)) return m_bool;
405 		else static if (is(T == double)) return m_float;
406 		else static if (is(T == float)) return cast(T)m_float;
407 		else static if (is(T == long)) return m_int;
408 		else static if (is(T == ulong)) return cast(ulong)m_int;
409 		else static if (is(T : long)){ enforceJson(m_int <= T.max && m_int >= T.min, "Integer conversion out of bounds error", m_fileName, line); return cast(T)m_int; }
410 		else static if (is(T == string)) return m_string;
411 		else static if (is(T == Json[])) return m_array;
412 		else static if (is(T == Json[string])) return m_object;
413 		else static assert("JSON can only be cast to (bool, long, double, string, Json[] or Json[string]. Not "~T.stringof~".");
414 	}
415 
416 	/**
417 		Returns the native type for this JSON if it matches the current runtime type.
418 
419 		If the runtime type does not match the given native type, the 'def' parameter is returned
420 		instead.
421 
422 		See_Also: `get`
423 	*/
424 	@property const(T) opt(T)(const(T) def = T.init)
425 	const {
426 		if( typeId!T != m_type ) return def;
427 		return get!T;
428 	}
429 	/// ditto
430 	@property T opt(T)(T def = T.init)
431 	{
432 		if( typeId!T != m_type ) return def;
433 		return get!T;
434 	}
435 
436 	/**
437 		Converts the JSON value to the corresponding D type - types are converted as necessary.
438 
439 		Automatically performs conversions between strings and numbers. See
440 		`get` for the list of available types. For converting/deserializing
441 		JSON to complex data types see `deserializeJson`.
442 
443 		See_Also: `get`, `deserializeJson`
444 	*/
445 	@property inout(T) to(T)()
446 	inout {
447 		static if( is(T == bool) ){
448 			final switch( m_type ){
449 				case Type.undefined: return false;
450 				case Type.null_: return false;
451 				case Type.bool_: return m_bool;
452 				case Type.int_: return m_int != 0;
453 				case Type.float_: return m_float != 0;
454 				case Type..string: return m_string.length > 0;
455 				case Type.array: return m_array.length > 0;
456 				case Type.object: return m_object.length > 0;
457 			}
458 		} else static if( is(T == double) ){
459 			final switch( m_type ){
460 				case Type.undefined: return T.init;
461 				case Type.null_: return 0;
462 				case Type.bool_: return m_bool ? 1 : 0;
463 				case Type.int_: return m_int;
464 				case Type.float_: return m_float;
465 				case Type..string: return .to!double(cast(string)m_string);
466 				case Type.array: return double.init;
467 				case Type.object: return double.init;
468 			}
469 		} else static if( is(T == float) ){
470 			final switch( m_type ){
471 				case Type.undefined: return T.init;
472 				case Type.null_: return 0;
473 				case Type.bool_: return m_bool ? 1 : 0;
474 				case Type.int_: return m_int;
475 				case Type.float_: return m_float;
476 				case Type..string: return .to!float(cast(string)m_string);
477 				case Type.array: return float.init;
478 				case Type.object: return float.init;
479 			}
480 		}
481 		else static if( is(T == long) ){
482 			final switch( m_type ){
483 				case Type.undefined: return 0;
484 				case Type.null_: return 0;
485 				case Type.bool_: return m_bool ? 1 : 0;
486 				case Type.int_: return m_int;
487 				case Type.float_: return cast(long)m_float;
488 				case Type..string: return .to!long(m_string);
489 				case Type.array: return 0;
490 				case Type.object: return 0;
491 			}
492 		} else static if( is(T : long) ){
493 			final switch( m_type ){
494 				case Type.undefined: return 0;
495 				case Type.null_: return 0;
496 				case Type.bool_: return m_bool ? 1 : 0;
497 				case Type.int_: return cast(T)m_int;
498 				case Type.float_: return cast(T)m_float;
499 				case Type..string: return cast(T).to!long(cast(string)m_string);
500 				case Type.array: return 0;
501 				case Type.object: return 0;
502 			}
503 		} else static if( is(T == string) ){
504 			switch( m_type ){
505 				default: return toString();
506 				case Type..string: return m_string;
507 			}
508 		} else static if( is(T == Json[]) ){
509 			switch( m_type ){
510 				default: return Json([this]);
511 				case Type.array: return m_array;
512 			}
513 		} else static if( is(T == Json[string]) ){
514 			switch( m_type ){
515 				default: return Json(["value": this]);
516 				case Type.object: return m_object;
517 			}
518 		} else static assert("JSON can only be cast to (bool, long, double, string, Json[] or Json[string]. Not "~T.stringof~".");
519 	}
520 
521 	/**
522 		Performs unary operations on the JSON value.
523 
524 		The following operations are supported for each type:
525 
526 		$(DL
527 			$(DT Null)   $(DD none)
528 			$(DT Bool)   $(DD ~)
529 			$(DT Int)    $(DD +, -, ++, --)
530 			$(DT Float)  $(DD +, -, ++, --)
531 			$(DT String) $(DD none)
532 			$(DT Array)  $(DD none)
533 			$(DT Object) $(DD none)
534 		)
535 	*/
536 	Json opUnary(string op)()
537 	const {
538 		static if( op == "~" ){
539 			checkType!bool();
540 			return Json(~m_bool);
541 		} else static if( op == "+" || op == "-" || op == "++" || op == "--" ){
542 			checkType!(long, double)("unary "~op);
543 			if( m_type == Type.int_ ) mixin("return Json("~op~"m_int);");
544 			else if( m_type == Type.float_ ) mixin("return Json("~op~"m_float);");
545 			else assert(false);
546 		} else static assert("Unsupported operator '"~op~"' for type JSON.");
547 	}
548 
549 	/**
550 		Performs binary operations between JSON values.
551 
552 		The two JSON values must be of the same run time type or a JSONException
553 		will be thrown. Only the operations listed are allowed for each of the
554 		types.
555 
556 		$(DL
557 			$(DT Null)   $(DD none)
558 			$(DT Bool)   $(DD &&, ||)
559 			$(DT Int)    $(DD +, -, *, /, %)
560 			$(DT Float)  $(DD +, -, *, /, %)
561 			$(DT String) $(DD ~)
562 			$(DT Array)  $(DD ~)
563 			$(DT Object) $(DD in)
564 		)
565 	*/
566 	Json opBinary(string op)(ref const(Json) other)
567 	const {
568 		enforceJson(m_type == other.m_type, "Binary operation '"~op~"' between "~.to!string(m_type)~" and "~.to!string(other.m_type)~" JSON objects.");
569 		static if( op == "&&" ){
570 			checkType!(bool)(op);
571 			return Json(m_bool && other.m_bool);
572 		} else static if( op == "||" ){
573 			checkType!(bool)(op);
574 			return Json(m_bool || other.m_bool);
575 		} else static if( op == "+" ){
576 			checkType!(long, double)(op);
577 			if( m_type == Type.Int ) return Json(m_int + other.m_int);
578 			else if( m_type == Type.float_ ) return Json(m_float + other.m_float);
579 			else assert(false);
580 		} else static if( op == "-" ){
581 			checkType!(long, double)(op);
582 			if( m_type == Type.Int ) return Json(m_int - other.m_int);
583 			else if( m_type == Type.float_ ) return Json(m_float - other.m_float);
584 			else assert(false);
585 		} else static if( op == "*" ){
586 			checkType!(long, double)(op);
587 			if( m_type == Type.Int ) return Json(m_int * other.m_int);
588 			else if( m_type == Type.float_ ) return Json(m_float * other.m_float);
589 			else assert(false);
590 		} else static if( op == "/" ){
591 			checkType!(long, double)(op);
592 			if( m_type == Type.Int ) return Json(m_int / other.m_int);
593 			else if( m_type == Type.float_ ) return Json(m_float / other.m_float);
594 			else assert(false);
595 		} else static if( op == "%" ){
596 			checkType!(long, double)(op);
597 			if( m_type == Type.Int ) return Json(m_int % other.m_int);
598 			else if( m_type == Type.float_ ) return Json(m_float % other.m_float);
599 			else assert(false);
600 		} else static if( op == "~" ){
601 			checkType!(string, Json[])(op);
602 			if( m_type == Type..string ) return Json(m_string ~ other.m_string);
603 			else if (m_type == Type.array) return Json(m_array ~ other.m_array);
604 			else assert(false);
605 		} else static assert("Unsupported operator '"~op~"' for type JSON.");
606 	}
607 	/// ditto
608 	Json opBinary(string op)(Json other)
609 		if( op == "~" )
610 	{
611 		static if( op == "~" ){
612 			checkType!(string, Json[])(op);
613 			if( m_type == Type..string ) return Json(m_string ~ other.m_string);
614 			else if( m_type == Type.array ) return Json(m_array ~ other.m_array);
615 			else assert(false);
616 		} else static assert("Unsupported operator '"~op~"' for type JSON.");
617 	}
618 	/// ditto
619 	void opOpAssign(string op)(Json other)
620 		if (op == "+" || op == "-" || op == "*" || op == "/" || op == "%" || op =="~")
621 	{
622 		enforceJson(m_type == other.m_type || op == "~" && m_type == Type.array,
623 				"Binary operation '"~op~"=' between "~.to!string(m_type)~" and "~.to!string(other.m_type)~" JSON objects.");
624 		static if( op == "+" ){
625 			if( m_type == Type.int_ ) m_int += other.m_int;
626 			else if( m_type == Type.float_ ) m_float += other.m_float;
627 			else enforceJson(false, "'+=' only allowed for scalar types, not "~.to!string(m_type)~".");
628 		} else static if( op == "-" ){
629 			if( m_type == Type.int_ ) m_int -= other.m_int;
630 			else if( m_type == Type.float_ ) m_float -= other.m_float;
631 			else enforceJson(false, "'-=' only allowed for scalar types, not "~.to!string(m_type)~".");
632 		} else static if( op == "*" ){
633 			if( m_type == Type.int_ ) m_int *= other.m_int;
634 			else if( m_type == Type.float_ ) m_float *= other.m_float;
635 			else enforceJson(false, "'*=' only allowed for scalar types, not "~.to!string(m_type)~".");
636 		} else static if( op == "/" ){
637 			if( m_type == Type.int_ ) m_int /= other.m_int;
638 			else if( m_type == Type.float_ ) m_float /= other.m_float;
639 			else enforceJson(false, "'/=' only allowed for scalar types, not "~.to!string(m_type)~".");
640 		} else static if( op == "%" ){
641 			if( m_type == Type.int_ ) m_int %= other.m_int;
642 			else if( m_type == Type.float_ ) m_float %= other.m_float;
643 			else enforceJson(false, "'%=' only allowed for scalar types, not "~.to!string(m_type)~".");
644 		} else static if( op == "~" ){
645 			if (m_type == Type..string) m_string ~= other.m_string;
646 			else if (m_type == Type.array) {
647 				if (other.m_type == Type.array) m_array ~= other.m_array;
648 				else appendArrayElement(other);
649 			} else enforceJson(false, "'~=' only allowed for string and array types, not "~.to!string(m_type)~".");
650 		} else static assert("Unsupported operator '"~op~"=' for type JSON.");
651 	}
652 	/// ditto
653 	void opOpAssign(string op, T)(T other)
654 		if (!is(T == Json) && is(typeof(Json(other))))
655 	{
656 		opOpAssign!op(Json(other));
657 	}
658 	/// ditto
659 	Json opBinary(string op)(bool other) const { checkType!bool(); mixin("return Json(m_bool "~op~" other);"); }
660 	/// ditto
661 	Json opBinary(string op)(long other) const { checkType!long(); mixin("return Json(m_int "~op~" other);"); }
662 	/// ditto
663 	Json opBinary(string op)(double other) const { checkType!double(); mixin("return Json(m_float "~op~" other);"); }
664 	/// ditto
665 	Json opBinary(string op)(string other) const { checkType!string(); mixin("return Json(m_string "~op~" other);"); }
666 	/// ditto
667 	Json opBinary(string op)(Json[] other) { checkType!(Json[])(); mixin("return Json(m_array "~op~" other);"); }
668 	/// ditto
669 	Json opBinaryRight(string op)(bool other) const { checkType!bool(); mixin("return Json(other "~op~" m_bool);"); }
670 	/// ditto
671 	Json opBinaryRight(string op)(long other) const { checkType!long(); mixin("return Json(other "~op~" m_int);"); }
672 	/// ditto
673 	Json opBinaryRight(string op)(double other) const { checkType!double(); mixin("return Json(other "~op~" m_float);"); }
674 	/// ditto
675 	Json opBinaryRight(string op)(string other) const if(op == "~") { checkType!string(); return Json(other ~ m_string); }
676 	/// ditto
677 	inout(Json)* opBinaryRight(string op)(string other) inout if(op == "in") {
678 		checkType!(Json[string])();
679 		auto pv = other in m_object;
680 		if( !pv ) return null;
681 		if( pv.type == Type.undefined ) return null;
682 		return pv;
683 	}
684 	/// ditto
685 	Json opBinaryRight(string op)(Json[] other) { checkType!(Json[])(); mixin("return Json(other "~op~" m_array);"); }
686 
687 	/**
688 	 * The append operator will append arrays. This method always appends it's argument as an array element, so nested arrays can be created.
689 	 */
690 	void appendArrayElement(Json element)
691 	{
692 		enforceJson(m_type == Type.array, "'appendArrayElement' only allowed for array types, not "~.to!string(m_type)~".");
693 		m_array ~= element;
694 	}
695 
696 	/**
697 		Compares two JSON values for equality.
698 
699 		If the two values have different types, they are considered unequal.
700 		This differs with ECMA script, which performs a type conversion before
701 		comparing the values.
702 	*/
703 	bool opEquals(ref const Json other)
704 	const {
705 		if( m_type != other.m_type ) return false;
706 		final switch(m_type){
707 			case Type.undefined: return false;
708 			case Type.null_: return true;
709 			case Type.bool_: return m_bool == other.m_bool;
710 			case Type.int_: return m_int == other.m_int;
711 			case Type.float_: return m_float == other.m_float;
712 			case Type..string: return m_string == other.m_string;
713 			case Type.array: return m_array == other.m_array;
714 			case Type.object: return m_object == other.m_object;
715 		}
716 	}
717 	/// ditto
718 	bool opEquals(const Json other) const { return opEquals(other); }
719 	/// ditto
720 	bool opEquals(typeof(null)) const { return m_type == Type.null_; }
721 	/// ditto
722 	bool opEquals(bool v) const { return m_type == Type.bool_ && m_bool == v; }
723 	/// ditto
724 	bool opEquals(int v) const { return m_type == Type.int_ && m_int == v; }
725 	/// ditto
726 	bool opEquals(long v) const { return m_type == Type.int_ && m_int == v; }
727 	/// ditto
728 	bool opEquals(double v) const { return m_type == Type.float_ && m_float == v; }
729 	/// ditto
730 	bool opEquals(string v) const { return m_type == Type..string && m_string == v; }
731 
732 	/**
733 		Compares two JSON values.
734 
735 		If the types of the two values differ, the value with the smaller type
736 		id is considered the smaller value. This differs from ECMA script, which
737 		performs a type conversion before comparing the values.
738 
739 		JSON values of type Object cannot be compared and will throw an
740 		exception.
741 	*/
742 	int opCmp(ref const Json other)
743 	const {
744 		if( m_type != other.m_type ) return m_type < other.m_type ? -1 : 1;
745 		final switch(m_type){
746 			case Type.undefined: return 0;
747 			case Type.null_: return 0;
748 			case Type.bool_: return m_bool < other.m_bool ? -1 : m_bool == other.m_bool ? 0 : 1;
749 			case Type.int_: return m_int < other.m_int ? -1 : m_int == other.m_int ? 0 : 1;
750 			case Type.float_: return m_float < other.m_float ? -1 : m_float == other.m_float ? 0 : 1;
751 			case Type..string: return m_string < other.m_string ? -1 : m_string == other.m_string ? 0 : 1;
752 			case Type.array: return m_array < other.m_array ? -1 : m_array == other.m_array ? 0 : 1;
753 			case Type.object:
754 				enforceJson(false, "JSON objects cannot be compared.");
755 				assert(false);
756 		}
757 	}
758 
759 	alias opDollar = length;
760 
761 	/**
762 		Returns the type id corresponding to the given D type.
763 	*/
764 	static @property Type typeId(T)() {
765 		static if( is(T == typeof(null)) ) return Type.null_;
766 		else static if( is(T == bool) ) return Type.bool_;
767 		else static if( is(T == double) ) return Type.float_;
768 		else static if( is(T == float) ) return Type.float_;
769 		else static if( is(T : long) ) return Type.int_;
770 		else static if( is(T == string) ) return Type..string;
771 		else static if( is(T == Json[]) ) return Type.array;
772 		else static if( is(T == Json[string]) ) return Type.object;
773 		else static assert(false, "Unsupported JSON type '"~T.stringof~"'. Only bool, long, double, string, Json[] and Json[string] are allowed.");
774 	}
775 
776 	/**
777 		Returns the JSON object as a string.
778 
779 		For large JSON values use writeJsonString instead as this function will store the whole string
780 		in memory, whereas writeJsonString writes it out bit for bit.
781 
782 		See_Also: writeJsonString, toPrettyString
783 	*/
784 	string toString()
785 	const {
786 		auto ret = appender!string();
787 		writeJsonString(ret, this);
788 		return ret.data;
789 	}
790 
791 	/**
792 		Returns the JSON object as a "pretty" string.
793 
794 		---
795 		auto json = Json(["foo": Json("bar")]);
796 		writeln(json.toPrettyString());
797 
798 		// output:
799 		// {
800 		//     "foo": "bar"
801 		// }
802 		---
803 
804 		Params:
805 			level = Specifies the base amount of indentation for the output. Indentation  is always
806 				done using tab characters.
807 
808 		See_Also: writePrettyJsonString, toString
809 	*/
810 	string toPrettyString(int level = 0)
811 	const {
812 		auto ret = appender!string();
813 		writePrettyJsonString(ret, this, level);
814 		return ret.data;
815 	}
816 
817 	private void checkType(TYPES...)(string op = null)
818 	const {
819 		bool matched = false;
820 		foreach (T; TYPES) if (m_type == typeId!T) matched = true;
821 		if (matched) return;
822 
823 		string name;
824 		version (VibeJsonFieldNames) {
825 			if (m_name.length) name = m_name ~ " of type " ~ m_type.to!string;
826 			else name = "JSON of type " ~ m_type.to!string;
827 		} else name = "JSON of type " ~ m_type.to!string;
828 
829 		string expected;
830 		static if (TYPES.length == 1) expected = typeId!(TYPES[0]).to!string;
831 		else {
832 			foreach (T; TYPES) {
833 				if (expected.length > 0) expected ~= ", ";
834 				expected ~= typeId!T.to!string;
835 			}
836 		}
837 
838 		enforceJson(op.length > 0, format("Got %s, expected %s.", name, expected), m_fileName, line);
839 		enforceJson(false, format("Got %s, expected %s for %s.", name, expected, op), m_fileName, line);
840 	}
841 
842 	/*invariant()
843 	{
844 		assert(m_type >= Type.Undefined && m_type <= Type.Object);
845 	}*/
846 }
847 
848 
849 /******************************************************************************/
850 /* public functions                                                           */
851 /******************************************************************************/
852 
853 /**
854 	Parses the given range as a JSON string and returns the corresponding Json object.
855 
856 	The range is shrunk during parsing, leaving any remaining text that is not part of
857 	the JSON contents.
858 
859 	Throws a JSONException if any parsing error occurred.
860 */
861 Json parseJson(R)(ref R range, int* line = null, string filename = null)
862 	if( is(R == string) )
863 {
864 	import std.string : startsWith;
865 
866 	Json ret;
867 	enforceJson(!range.empty, "JSON string is empty.", filename, 0);
868 
869 	skipWhitespace(range, line);
870 
871 	version(JsonLineNumbers) {
872 		int curline = line ? *line : 0;
873 	}
874 
875 	switch( range.front ){
876 		case 'f':
877 			enforceJson(range[1 .. $].startsWith("alse"), "Expected 'false', got '"~range[0 .. min(5, $)]~"'.", filename, line);
878 			range.popFrontN(5);
879 			ret = false;
880 			break;
881 		case 'n':
882 			enforceJson(range[1 .. $].startsWith("ull"), "Expected 'null', got '"~range[0 .. min(4, $)]~"'.", filename, line);
883 			range.popFrontN(4);
884 			ret = null;
885 			break;
886 		case 't':
887 			enforceJson(range[1 .. $].startsWith("rue"), "Expected 'true', got '"~range[0 .. min(4, $)]~"'.", filename, line);
888 			range.popFrontN(4);
889 			ret = true;
890 			break;
891 		case '0': .. case '9':
892 		case '-':
893 			bool is_float;
894 			auto num = skipNumber(range, is_float, filename, line);
895 			if( is_float ) ret = to!double(num);
896 			else ret = to!long(num);
897 			break;
898 		case '\"':
899 			ret = skipJsonString(range, filename, line);
900 			break;
901 		case '[':
902 			Json[] arr;
903 			range.popFront();
904 			while (true) {
905 				skipWhitespace(range, line);
906 				enforceJson(!range.empty, "Missing ']' before EOF.", filename, line);
907 				if(range.front == ']') break;
908 				arr ~= parseJson(range, line, filename);
909 				skipWhitespace(range, line);
910 				enforceJson(!range.empty, "Missing ']' before EOF.", filename, line);
911 				enforceJson(range.front == ',' || range.front == ']',
912 					format("Expected ']' or ',' - got '%s'.", range.front), filename, line);
913 				if( range.front == ']' ) break;
914 				else range.popFront();
915 			}
916 			range.popFront();
917 			ret = arr;
918 			break;
919 		case '{':
920 			Json[string] obj;
921 			range.popFront();
922 			while (true) {
923 				skipWhitespace(range, line);
924 				enforceJson(!range.empty, "Missing '}' before EOF.", filename, line);
925 				if(range.front == '}') break;
926 				string key = skipJsonString(range, filename, line);
927 				skipWhitespace(range, line);
928 				enforceJson(range.startsWith(":"), "Expected ':' for key '" ~ key ~ "'", filename, line);
929 				range.popFront();
930 				skipWhitespace(range, line);
931 				Json itm = parseJson(range, line, filename);
932 				obj[key] = itm;
933 				skipWhitespace(range, line);
934 				enforceJson(!range.empty, "Missing '}' before EOF.", filename, line);
935 				enforceJson(range.front == ',' || range.front == '}',
936 					format("Expected '}' or ',' - got '%s'.", range.front), filename, line);
937 				if (range.front == '}') break;
938 				else range.popFront();
939 			}
940 			range.popFront();
941 			ret = obj;
942 			break;
943 		default:
944 			enforceJson(false, format("Expected valid JSON token, got '%s'.", range[0 .. min(12, $)]), filename, line);
945 			assert(false);
946 	}
947 
948 	assert(ret.type != Json.Type.undefined);
949 	version(JsonLineNumbers) ret.line = curline;
950 	ret.m_fileName = filename;
951 	return ret;
952 }
953 
954 /**
955 	Parses the given JSON string and returns the corresponding Json object.
956 
957 	Throws a JSONException if any parsing error occurs.
958 */
959 Json parseJsonString(string str, string filename = null)
960 {
961 	import std.string : strip;
962 
963 	auto strcopy = str;
964 	int line = 0;
965 	auto ret = parseJson(strcopy, &line, filename);
966 	enforceJson(strcopy.strip().length == 0, "Expected end of string after JSON value.", filename, line);
967 	return ret;
968 }
969 
970 unittest {
971 	assert(parseJsonString("null") == Json(null));
972 	assert(parseJsonString("true") == Json(true));
973 	assert(parseJsonString("false") == Json(false));
974 	assert(parseJsonString("1") == Json(1));
975 	assert(parseJsonString("2.0") == Json(2.0));
976 	assert(parseJsonString("\"test\"") == Json("test"));
977 	assert(parseJsonString("[1, 2, 3]") == Json([Json(1), Json(2), Json(3)]));
978 	assert(parseJsonString("{\"a\": 1}") == Json(["a": Json(1)]));
979 	assert(parseJsonString(`"\\\/\b\f\n\r\t\u1234"`).get!string == "\\/\b\f\n\r\t\u1234");
980 	auto json = parseJsonString(`{"hey": "This is @à test éhééhhéhéé !%/??*&?\ud83d\udcec"}`);
981 	assert(json.toPrettyString() == parseJsonString(json.toPrettyString()).toPrettyString());
982 }
983 
984 unittest {
985 	import std.string : endsWith;
986 
987 	try parseJsonString(`{"a": 1`);
988 	catch (Exception e) assert(e.msg.endsWith("Missing '}' before EOF."));
989 	try parseJsonString(`{"a": 1 x`);
990 	catch (Exception e) assert(e.msg.endsWith("Expected '}' or ',' - got 'x'."));
991 	try parseJsonString(`[1`);
992 	catch (Exception e) assert(e.msg.endsWith("Missing ']' before EOF."));
993 	try parseJsonString(`[1 x`);
994 	catch (Exception e) assert(e.msg.endsWith("Expected ']' or ',' - got 'x'."));
995 }
996 
997 /**
998 	Serializes the given value to JSON.
999 
1000 	The following types of values are supported:
1001 
1002 	$(DL
1003 		$(DT `Json`)            $(DD Used as-is)
1004 		$(DT `null`)            $(DD Converted to `Json.Type.null_`)
1005 		$(DT `bool`)            $(DD Converted to `Json.Type.bool_`)
1006 		$(DT `float`, `double`)   $(DD Converted to `Json.Type.float_`)
1007 		$(DT `short`, `ushort`, `int`, `uint`, `long`, `ulong`) $(DD Converted to `Json.Type.int_`)
1008 		$(DT `string`)          $(DD Converted to `Json.Type.string`)
1009 		$(DT `T[]`)             $(DD Converted to `Json.Type.array`)
1010 		$(DT `T[string]`)       $(DD Converted to `Json.Type.object`)
1011 		$(DT `struct`)          $(DD Converted to `Json.Type.object`)
1012 		$(DT `class`)           $(DD Converted to `Json.Type.object` or `Json.Type.null_`)
1013 	)
1014 
1015 	All entries of an array or an associative array, as well as all R/W properties and
1016 	all public fields of a struct/class are recursively serialized using the same rules.
1017 
1018 	Fields ending with an underscore will have the last underscore stripped in the
1019 	serialized output. This makes it possible to use fields with D keywords as their name
1020 	by simply appending an underscore.
1021 
1022 	The following methods can be used to customize the serialization of structs/classes:
1023 
1024 	---
1025 	Json toJson() const;
1026 	static T fromJson(Json src);
1027 
1028 	string toString() const;
1029 	static T fromString(string src);
1030 	---
1031 
1032 	The methods will have to be defined in pairs. The first pair that is implemented by
1033 	the type will be used for serialization (i.e. `toJson` overrides `toString`).
1034 
1035 	See_Also: `deserializeJson`, `vibe.data.serialization`
1036 */
1037 Json serializeToJson(T)(T value)
1038 {
1039 	version (VibeOldSerialization) {
1040 		return serializeToJsonOld(value);
1041 	} else {
1042 		return serialize!JsonSerializer(value);
1043 	}
1044 }
1045 /// ditto
1046 void serializeToJson(R, T)(R destination, T value)
1047 	if (isOutputRange!(R, char) || isOutputRange!(R, ubyte))
1048 {
1049 	serialize!(JsonStringSerializer!R)(value, destination);
1050 }
1051 /// ditto
1052 string serializeToJsonString(T)(T value)
1053 {
1054 	auto ret = appender!string;
1055 	serializeToJson(ret, value);
1056 	return ret.data;
1057 }
1058 
1059 ///
1060 unittest {
1061 	struct Foo {
1062 		int number;
1063 		string str;
1064 	}
1065 
1066 	Foo f;
1067 	f.number = 12;
1068 	f.str = "hello";
1069 
1070 	string json = serializeToJsonString(f);
1071 	assert(json == `{"number":12,"str":"hello"}`);
1072 
1073 	Json jsonval = serializeToJson(f);
1074 	assert(jsonval.type == Json.Type.object);
1075 	assert(jsonval["number"] == Json(12));
1076 	assert(jsonval["str"] == Json("hello"));
1077 }
1078 
1079 
1080 /**
1081 	Serializes the given value to a pretty printed JSON string.
1082 
1083 	See_also: `serializeToJson`, `vibe.data.serialization`
1084 */
1085 void serializeToPrettyJson(R, T)(R destination, T value)
1086 	if (isOutputRange!(R, char) || isOutputRange!(R, ubyte))
1087 {
1088 	serialize!(JsonStringSerializer!(R, true))(value, destination);
1089 }
1090 /// ditto
1091 string serializeToPrettyJson(T)(T value)
1092 {
1093 	auto ret = appender!string;
1094 	serializeToPrettyJson(ret, value);
1095 	return ret.data;
1096 }
1097 
1098 ///
1099 unittest {
1100 	struct Foo {
1101 		int number;
1102 		string str;
1103 	}
1104 
1105 	Foo f;
1106 	f.number = 12;
1107 	f.str = "hello";
1108 
1109 	string json = serializeToPrettyJson(f);
1110 	assert(json ==
1111 `{
1112 	"number": 12,
1113 	"str": "hello"
1114 }`);
1115 }
1116 
1117 
1118 /// private
1119 Json serializeToJsonOld(T)(T value)
1120 {
1121 	import vibe.internal.meta.traits;
1122 
1123 	alias TU = Unqual!T;
1124 	static if (is(TU == Json)) return value;
1125 	else static if (is(TU == typeof(null))) return Json(null);
1126 	else static if (is(TU == bool)) return Json(value);
1127 	else static if (is(TU == float)) return Json(cast(double)value);
1128 	else static if (is(TU == double)) return Json(value);
1129 	else static if (is(TU == DateTime)) return Json(value.toISOExtString());
1130 	else static if (is(TU == SysTime)) return Json(value.toISOExtString());
1131 	else static if (is(TU == Date)) return Json(value.toISOExtString());
1132 	else static if (is(TU : long)) return Json(cast(long)value);
1133 	else static if (is(TU : string)) return Json(value);
1134 	else static if (isArray!T) {
1135 		auto ret = new Json[value.length];
1136 		foreach (i; 0 .. value.length)
1137 			ret[i] = serializeToJson(value[i]);
1138 		return Json(ret);
1139 	} else static if (isAssociativeArray!TU) {
1140 		Json[string] ret;
1141 		alias TK = KeyType!T;
1142 		foreach (key, value; value) {
1143 			static if(is(TK == string)) {
1144 				ret[key] = serializeToJson(value);
1145 			} else static if (is(TK == enum)) {
1146 				ret[to!string(key)] = serializeToJson(value);
1147 			} else static if (isStringSerializable!(TK)) {
1148 				ret[key.toString()] = serializeToJson(value);
1149 			} else static assert("AA key type %s not supported for JSON serialization.");
1150 		}
1151 		return Json(ret);
1152 	} else static if (isJsonSerializable!TU) {
1153 		return value.toJson();
1154 	} else static if (isStringSerializable!TU) {
1155 		return Json(value.toString());
1156 	} else static if (is(TU == struct)) {
1157 		Json[string] ret;
1158 		foreach (m; __traits(allMembers, T)) {
1159 			static if (isRWField!(TU, m)) {
1160 				auto mv = __traits(getMember, value, m);
1161 				ret[underscoreStrip(m)] = serializeToJson(mv);
1162 			}
1163 		}
1164 		return Json(ret);
1165 	} else static if(is(TU == class)) {
1166 		if (value is null) return Json(null);
1167 		Json[string] ret;
1168 		foreach (m; __traits(allMembers, T)) {
1169 			static if (isRWField!(TU, m)) {
1170 				auto mv = __traits(getMember, value, m);
1171 				ret[underscoreStrip(m)] = serializeToJson(mv);
1172 			}
1173 		}
1174 		return Json(ret);
1175 	} else static if (isPointer!TU) {
1176 		if (value is null) return Json(null);
1177 		return serializeToJson(*value);
1178 	} else {
1179 		static assert(false, "Unsupported type '"~T.stringof~"' for JSON serialization.");
1180 	}
1181 }
1182 
1183 
1184 /**
1185 	Deserializes a JSON value into the destination variable.
1186 
1187 	The same types as for `serializeToJson()` are supported and handled inversely.
1188 
1189 	See_Also: `serializeToJson`, `serializeToJsonString`, `vibe.data.serialization`
1190 */
1191 void deserializeJson(T)(ref T dst, Json src)
1192 {
1193 	dst = deserializeJson!T(src);
1194 }
1195 /// ditto
1196 T deserializeJson(T)(Json src)
1197 {
1198 	version (VibeOldSerialization) {
1199 		return deserializeJsonOld!T(src);
1200 	} else {
1201 		return deserialize!(JsonSerializer, T)(src);
1202 	}
1203 }
1204 /// ditto
1205 T deserializeJson(T, R)(R input)
1206 	if (isInputRange!R && !is(R == Json))
1207 {
1208 	return deserialize!(JsonStringSerializer!R, T)(input);
1209 }
1210 
1211 /// private
1212 T deserializeJsonOld(T)(Json src)
1213 {
1214 	import vibe.internal.meta.traits;
1215 
1216 	static if( is(T == struct) || isSomeString!T || isIntegral!T || isFloatingPoint!T )
1217 		if( src.type == Json.Type.null_ ) return T.init;
1218 	static if (is(T == Json)) return src;
1219 	else static if (is(T == typeof(null))) { return null; }
1220 	else static if (is(T == bool)) return src.get!bool;
1221 	else static if (is(T == float)) return src.to!float;   // since doubles are frequently serialized without
1222 	else static if (is(T == double)) return src.to!double; // a decimal point, we allow conversions here
1223 	else static if (is(T == DateTime)) return DateTime.fromISOExtString(src.get!string);
1224 	else static if (is(T == SysTime)) return SysTime.fromISOExtString(src.get!string);
1225 	else static if (is(T == Date)) return Date.fromISOExtString(src.get!string);
1226 	else static if (is(T : long)) return cast(T)src.get!long;
1227 	else static if (is(T : string)) return cast(T)src.get!string;
1228 	else static if (isArray!T) {
1229 		alias TV = typeof(T.init[0]) ;
1230 		auto dst = new Unqual!TV[src.length];
1231 		foreach (size_t i, v; src)
1232 			dst[i] = deserializeJson!(Unqual!TV)(v);
1233 		return cast(T)dst;
1234 	} else static if( isAssociativeArray!T ) {
1235 		alias TV = typeof(T.init.values[0]) ;
1236 		alias TK = KeyType!T;
1237 		Unqual!TV[TK] dst;
1238 		foreach (string key, value; src) {
1239 			static if (is(TK == string)) {
1240 				dst[key] = deserializeJson!(Unqual!TV)(value);
1241 			} else static if (is(TK == enum)) {
1242 				dst[to!(TK)(key)] = deserializeJson!(Unqual!TV)(value);
1243 			} else static if (isStringSerializable!TK) {
1244 				auto dsk = TK.fromString(key);
1245 				dst[dsk] = deserializeJson!(Unqual!TV)(value);
1246 			} else static assert("AA key type %s not supported for JSON serialization.");
1247 		}
1248 		return dst;
1249 	} else static if (isJsonSerializable!T) {
1250 		return T.fromJson(src);
1251 	} else static if (isStringSerializable!T) {
1252 		return T.fromString(src.get!string);
1253 	} else static if (is(T == struct)) {
1254 		T dst;
1255 		foreach (m; __traits(allMembers, T)) {
1256 			static if (isRWPlainField!(T, m) || isRWField!(T, m)) {
1257 				alias TM = typeof(__traits(getMember, dst, m)) ;
1258 				__traits(getMember, dst, m) = deserializeJson!TM(src[underscoreStrip(m)]);
1259 			}
1260 		}
1261 		return dst;
1262 	} else static if (is(T == class)) {
1263 		if (src.type == Json.Type.null_) return null;
1264 		auto dst = new T;
1265 		foreach (m; __traits(allMembers, T)) {
1266 			static if (isRWPlainField!(T, m) || isRWField!(T, m)) {
1267 				alias TM = typeof(__traits(getMember, dst, m)) ;
1268 				__traits(getMember, dst, m) = deserializeJson!TM(src[underscoreStrip(m)]);
1269 			}
1270 		}
1271 		return dst;
1272 	} else static if (isPointer!T) {
1273 		if (src.type == Json.Type.null_) return null;
1274 		alias TD = typeof(*T.init) ;
1275 		dst = new TD;
1276 		*dst = deserializeJson!TD(src);
1277 		return dst;
1278 	} else {
1279 		static assert(false, "Unsupported type '"~T.stringof~"' for JSON serialization.");
1280 	}
1281 }
1282 
1283 ///
1284 unittest {
1285 	struct Foo {
1286 		int number;
1287 		string str;
1288 	}
1289 
1290 	Foo f = deserializeJson!Foo(`{"number": 12, "str": "hello"}`);
1291 	assert(f.number == 12);
1292 	assert(f.str == "hello");
1293 }
1294 
1295 unittest {
1296 	import std.stdio;
1297 	enum Foo : string { k = "test" }
1298 	enum Boo : int { l = 5 }
1299 	static struct S { float a; double b; bool c; int d; string e; byte f; ubyte g; long h; ulong i; float[] j; Foo k; Boo l; }
1300 	immutable S t = {1.5, -3.0, true, int.min, "Test", -128, 255, long.min, ulong.max, [1.1, 1.2, 1.3], Foo.k, Boo.l};
1301 	S u;
1302 	deserializeJson(u, serializeToJson(t));
1303 	assert(t.a == u.a);
1304 	assert(t.b == u.b);
1305 	assert(t.c == u.c);
1306 	assert(t.d == u.d);
1307 	assert(t.e == u.e);
1308 	assert(t.f == u.f);
1309 	assert(t.g == u.g);
1310 	assert(t.h == u.h);
1311 	assert(t.i == u.i);
1312 	assert(t.j == u.j);
1313 	assert(t.k == u.k);
1314 	assert(t.l == u.l);
1315 }
1316 
1317 unittest
1318 {
1319 	assert(uint.max == serializeToJson(uint.max).deserializeJson!uint);
1320 	assert(ulong.max == serializeToJson(ulong.max).deserializeJson!ulong);
1321 }
1322 
1323 unittest {
1324 	static struct A { int value; static A fromJson(Json val) { return A(val.get!int); } Json toJson() const { return Json(value); } }
1325 	static struct C { int value; static C fromString(string val) { return C(val.to!int); } string toString() const { return value.to!string; } }
1326 	static struct D { int value; }
1327 
1328 	assert(serializeToJson(const A(123)) == Json(123));
1329 	assert(serializeToJson(A(123))       == Json(123));
1330 	assert(serializeToJson(const C(123)) == Json("123"));
1331 	assert(serializeToJson(C(123))       == Json("123"));
1332 	assert(serializeToJson(const D(123)) == serializeToJson(["value": 123]));
1333 	assert(serializeToJson(D(123))       == serializeToJson(["value": 123]));
1334 }
1335 
1336 unittest {
1337 	auto d = Date(2001,1,1);
1338 	deserializeJson(d, serializeToJson(Date.init));
1339 	assert(d == Date.init);
1340 	deserializeJson(d, serializeToJson(Date(2001,1,1)));
1341 	assert(d == Date(2001,1,1));
1342 	struct S { immutable(int)[] x; }
1343 	S s;
1344 	deserializeJson(s, serializeToJson(S([1,2,3])));
1345 	assert(s == S([1,2,3]));
1346 	struct T {
1347 		@optional S s;
1348 		@optional int i;
1349 		@optional float f_; // underscore strip feature
1350 		@optional double d;
1351 		@optional string str;
1352 	}
1353 	auto t = T(S([1,2,3]));
1354 	deserializeJson(t, parseJsonString(`{ "s" : null, "i" : null, "f" : null, "d" : null, "str" : null }`));
1355 	assert(text(t) == text(T()));
1356 }
1357 
1358 unittest {
1359 	static class C {
1360 		int a;
1361 		private int _b;
1362 		@property int b() const { return _b; }
1363 		@property void b(int v) { _b = v; }
1364 
1365 		@property int test() const { return 10; }
1366 
1367 		void test2() {}
1368 	}
1369 	C c = new C;
1370 	c.a = 1;
1371 	c.b = 2;
1372 
1373 	C d;
1374 	deserializeJson(d, serializeToJson(c));
1375 	assert(c.a == d.a);
1376 	assert(c.b == d.b);
1377 }
1378 
1379 unittest {
1380 	static struct C { int value; static C fromString(string val) { return C(val.to!int); } string toString() const { return value.to!string; } }
1381 	enum Color { Red, Green, Blue }
1382 	{
1383 		static class T {
1384 			string[Color] enumIndexedMap;
1385 			string[C] stringableIndexedMap;
1386 			this() {
1387 				enumIndexedMap = [ Color.Red : "magenta", Color.Blue : "deep blue" ];
1388                                 stringableIndexedMap = [ C(42) : "forty-two" ];
1389 			}
1390 		}
1391 
1392 		T original = new T;
1393 		original.enumIndexedMap[Color.Green] = "olive";
1394 		T other;
1395 		deserializeJson(other, serializeToJson(original));
1396 		assert(serializeToJson(other) == serializeToJson(original));
1397 	}
1398 	{
1399 		static struct S {
1400 			string[Color] enumIndexedMap;
1401 			string[C] stringableIndexedMap;
1402 		}
1403 
1404 		S *original = new S;
1405 		original.enumIndexedMap = [ Color.Red : "magenta", Color.Blue : "deep blue" ];
1406 		original.enumIndexedMap[Color.Green] = "olive";
1407                 original.stringableIndexedMap = [ C(42) : "forty-two" ];
1408 		S other;
1409 		deserializeJson(other, serializeToJson(original));
1410 		assert(serializeToJson(other) == serializeToJson(original));
1411 	}
1412 }
1413 
1414 unittest {
1415 	import std.typecons : Nullable;
1416 
1417 	struct S { Nullable!int a, b; }
1418 	S s;
1419 	s.a = 2;
1420 
1421 	auto j = serializeToJson(s);
1422 	assert(j["a"].type == Json.Type.int_);
1423 	assert(j["b"].type == Json.Type.null_);
1424 
1425 	auto t = deserializeJson!S(j);
1426 	assert(!t.a.isNull() && t.a == 2);
1427 	assert(t.b.isNull());
1428 }
1429 
1430 unittest { // #840
1431 	int[2][2] nestedArray = 1;
1432 	assert(nestedArray.serializeToJson.deserializeJson!(typeof(nestedArray)) == nestedArray);
1433 }
1434 
1435 
1436 /**
1437 	Serializer for a plain Json representation.
1438 
1439 	See_Also: vibe.data.serialization.serialize, vibe.data.serialization.deserialize, serializeToJson, deserializeJson
1440 */
1441 struct JsonSerializer {
1442 	template isJsonBasicType(T) { enum isJsonBasicType = isNumeric!T || isBoolean!T || is(T == string) || is(T == typeof(null)) || isJsonSerializable!T; }
1443 
1444 	template isSupportedValueType(T) { enum isSupportedValueType = isJsonBasicType!T || is(T == Json); }
1445 
1446 	private {
1447 		Json m_current;
1448 		Json[] m_compositeStack;
1449 	}
1450 
1451 	this(Json data) { m_current = data; }
1452 
1453 	@disable this(this);
1454 
1455 	//
1456 	// serialization
1457 	//
1458 	Json getSerializedResult() { return m_current; }
1459 	void beginWriteDictionary(T)() { m_compositeStack ~= Json.emptyObject; }
1460 	void endWriteDictionary(T)() { m_current = m_compositeStack[$-1]; m_compositeStack.length--; }
1461 	void beginWriteDictionaryEntry(T)(string name) {}
1462 	void endWriteDictionaryEntry(T)(string name) { m_compositeStack[$-1][name] = m_current; }
1463 
1464 	void beginWriteArray(T)(size_t) { m_compositeStack ~= Json.emptyArray; }
1465 	void endWriteArray(T)() { m_current = m_compositeStack[$-1]; m_compositeStack.length--; }
1466 	void beginWriteArrayEntry(T)(size_t) {}
1467 	void endWriteArrayEntry(T)(size_t) { m_compositeStack[$-1].appendArrayElement(m_current); }
1468 
1469 	void writeValue(T)(T value)
1470 	{
1471 		static if (is(T == Json)) m_current = value;
1472 		else static if (isJsonSerializable!T) m_current = value.toJson();
1473 		else m_current = Json(value);
1474 	}
1475 
1476 	void writeValue(T)(in Json value) if (is(T == Json))
1477 	{
1478 		m_current = value.clone;
1479 	}
1480 
1481 	//
1482 	// deserialization
1483 	//
1484 	void readDictionary(T)(scope void delegate(string) field_handler)
1485 	{
1486 		enforceJson(m_current.type == Json.Type.object, "Expected JSON object, got "~m_current.type.to!string);
1487 		auto old = m_current;
1488 		foreach (string key, value; m_current) {
1489 			m_current = value;
1490 			field_handler(key);
1491 		}
1492 		m_current = old;
1493 	}
1494 
1495 	void readArray(T)(scope void delegate(size_t) size_callback, scope void delegate() entry_callback)
1496 	{
1497 		enforceJson(m_current.type == Json.Type.array, "Expected JSON array, got "~m_current.type.to!string);
1498 		auto old = m_current;
1499 		size_callback(m_current.length);
1500 		foreach (ent; old) {
1501 			m_current = ent;
1502 			entry_callback();
1503 		}
1504 		m_current = old;
1505 	}
1506 
1507 	T readValue(T)()
1508 	{
1509 		static if (is(T == Json)) return m_current;
1510 		else static if (isJsonSerializable!T) return T.fromJson(m_current);
1511 		else static if (is(T == float) || is(T == double)) {
1512 			if (m_current.type == Json.Type.undefined) return T.nan;
1513 			return m_current.type == Json.Type.float_ ? cast(T)m_current.get!double : cast(T)m_current.get!long;
1514 		}
1515 		else {
1516 			return m_current.get!T();
1517 		}
1518 	}
1519 
1520 	bool tryReadNull() { return m_current.type == Json.Type.null_; }
1521 }
1522 
1523 
1524 /**
1525 	Serializer for a range based plain JSON string representation.
1526 
1527 	See_Also: vibe.data.serialization.serialize, vibe.data.serialization.deserialize, serializeToJson, deserializeJson
1528 */
1529 struct JsonStringSerializer(R, bool pretty = false)
1530 	if (isInputRange!R || isOutputRange!(R, char))
1531 {
1532 	private {
1533 		R m_range;
1534 		size_t m_level = 0;
1535 	}
1536 
1537 	template isJsonBasicType(T) { enum isJsonBasicType = isNumeric!T || isBoolean!T || is(T == string) || is(T == typeof(null)) || isJsonSerializable!T; }
1538 
1539 	template isSupportedValueType(T) { enum isSupportedValueType = isJsonBasicType!T || is(T == Json); }
1540 
1541 	this(R range)
1542 	{
1543 		m_range = range;
1544 	}
1545 
1546 	@disable this(this);
1547 
1548 	//
1549 	// serialization
1550 	//
1551 	static if (isOutputRange!(R, char)) {
1552 		private {
1553 			bool m_firstInComposite;
1554 		}
1555 
1556 		void getSerializedResult() {}
1557 
1558 		void beginWriteDictionary(T)() { startComposite(); m_range.put('{'); }
1559 		void endWriteDictionary(T)() { endComposite(); m_range.put("}"); }
1560 		void beginWriteDictionaryEntry(T)(string name)
1561 		{
1562 			startCompositeEntry();
1563 			m_range.put('"');
1564 			m_range.jsonEscape(name);
1565 			static if (pretty) m_range.put(`": `);
1566 			else m_range.put(`":`);
1567 		}
1568 		void endWriteDictionaryEntry(T)(string name) {}
1569 
1570 		void beginWriteArray(T)(size_t) { startComposite(); m_range.put('['); }
1571 		void endWriteArray(T)() { endComposite(); m_range.put(']'); }
1572 		void beginWriteArrayEntry(T)(size_t) { startCompositeEntry(); }
1573 		void endWriteArrayEntry(T)(size_t) {}
1574 
1575 		void writeValue(T)(in T value)
1576 		{
1577 			static if (is(T == typeof(null))) m_range.put("null");
1578 			else static if (is(T == bool)) m_range.put(value ? "true" : "false");
1579 			else static if (is(T : long)) m_range.formattedWrite("%s", value);
1580 			else static if (is(T : real)) m_range.formattedWrite("%.16g", value);
1581 			else static if (is(T == string)) {
1582 				m_range.put('"');
1583 				m_range.jsonEscape(value);
1584 				m_range.put('"');
1585 			}
1586 			else static if (is(T == Json)) m_range.writeJsonString(value);
1587 			else static if (isJsonSerializable!T) m_range.writeJsonString!(R, pretty)(value.toJson(), m_level);
1588 			else static assert(false, "Unsupported type: " ~ T.stringof);
1589 		}
1590 
1591 		private void startComposite()
1592 		{
1593 			static if (pretty) m_level++;
1594 			m_firstInComposite = true;
1595 		}
1596 
1597 		private void startCompositeEntry()
1598 		{
1599 			if (!m_firstInComposite) {
1600 				m_range.put(',');
1601 			} else {
1602 				m_firstInComposite = false;
1603 			}
1604 			static if (pretty) indent();
1605 		}
1606 
1607 		private void endComposite()
1608 		{
1609 			static if (pretty) {
1610 				m_level--;
1611 				if (!m_firstInComposite) indent();
1612 			}
1613 			m_firstInComposite = false;
1614 		}
1615 
1616 		private void indent()
1617 		{
1618 			m_range.put('\n');
1619 			foreach (i; 0 .. m_level) m_range.put('\t');
1620 		}
1621 	}
1622 
1623 	//
1624 	// deserialization
1625 	//
1626 	static if (isInputRange!(R)) {
1627 		private {
1628 			int m_line = 0;
1629 		}
1630 
1631 		void readDictionary(T)(scope void delegate(string) entry_callback)
1632 		{
1633 			m_range.skipWhitespace(&m_line);
1634 			enforceJson(!m_range.empty && m_range.front == '{', "Expecting object.");
1635 			m_range.popFront();
1636 			bool first = true;
1637 			while(true) {
1638 				m_range.skipWhitespace(&m_line);
1639 				enforceJson(!m_range.empty, "Missing '}'.");
1640 				if (m_range.front == '}') {
1641 					m_range.popFront();
1642 					break;
1643 				} else if (!first) {
1644 					enforceJson(m_range.front == ',', "Expecting ',' or '}', not '"~m_range.front.to!string~"'.");
1645 					m_range.popFront();
1646 					m_range.skipWhitespace(&m_line);
1647 				} else first = false;
1648 
1649 				auto name = m_range.skipJsonString(null, &m_line);
1650 
1651 				m_range.skipWhitespace(&m_line);
1652 				enforceJson(!m_range.empty && m_range.front == ':', "Expecting ':', not '"~m_range.front.to!string~"'.");
1653 				m_range.popFront();
1654 
1655 				entry_callback(name);
1656 			}
1657 		}
1658 
1659 		void readArray(T)(scope void delegate(size_t) size_callback, scope void delegate() entry_callback)
1660 		{
1661 			m_range.skipWhitespace(&m_line);
1662 			enforceJson(!m_range.empty && m_range.front == '[', "Expecting array.");
1663 			m_range.popFront();
1664 			bool first = true;
1665 			while(true) {
1666 				m_range.skipWhitespace(&m_line);
1667 				enforceJson(!m_range.empty, "Missing ']'.");
1668 				if (m_range.front == ']') {
1669 					m_range.popFront();
1670 					break;
1671 				} else if (!first) {
1672 					enforceJson(m_range.front == ',', "Expecting ',' or ']'.");
1673 					m_range.popFront();
1674 				} else first = false;
1675 
1676 				entry_callback();
1677 			}
1678 		}
1679 
1680 		T readValue(T)()
1681 		{
1682 			m_range.skipWhitespace(&m_line);
1683 			static if (is(T == typeof(null))) { enforceJson(m_range.take(4).equal("null"), "Expecting 'null'."); return null; }
1684 			else static if (is(T == bool)) {
1685 				bool ret = m_range.front == 't';
1686 				string expected = ret ? "true" : "false";
1687 				foreach (ch; expected) {
1688 					enforceJson(m_range.front == ch, "Expecting 'true' or 'false'.");
1689 					m_range.popFront();
1690 				}
1691 				return ret;
1692 			} else static if (is(T : long)) {
1693 				bool is_float;
1694 				auto num = m_range.skipNumber(is_float, null, &m_line);
1695 				enforceJson(!is_float, "Expecting integer number.");
1696 				return to!T(num);
1697 			} else static if (is(T : real)) {
1698 				bool is_float;
1699 				auto num = m_range.skipNumber(is_float);
1700 				return to!T(num);
1701 			}
1702 			else static if (is(T == string)) return m_range.skipJsonString(null, &m_line);
1703 			else static if (is(T == Json)) return m_range.parseJson(&m_line);
1704 			else static if (isJsonSerializable!T) return T.fromJson(m_range.parseJson(&m_line));
1705 			else static assert(false, "Unsupported type: " ~ T.stringof);
1706 		}
1707 
1708 		bool tryReadNull()
1709 		{
1710 			m_range.skipWhitespace(&m_line);
1711 			if (m_range.front != 'n') return false;
1712 			foreach (ch; "null") {
1713 				enforceJson(m_range.front == ch, "Expecting 'null'.");
1714 				m_range.popFront();
1715 			}
1716 			assert(m_range.empty || m_range.front != 'l');
1717 			return true;
1718 		}
1719 	}
1720 }
1721 
1722 
1723 
1724 /**
1725 	Writes the given JSON object as a JSON string into the destination range.
1726 
1727 	This function will convert the given JSON value to a string without adding
1728 	any white space between tokens (no newlines, no indentation and no padding).
1729 	The output size is thus minimized, at the cost of bad human readability.
1730 
1731 	Params:
1732 		dst   = References the string output range to which the result is written.
1733 		json  = Specifies the JSON value that is to be stringified.
1734 		level = The nesting level at which to write the JSON object (for pretty output).
1735 
1736 	See_Also: Json.toString, writePrettyJsonString
1737 */
1738 void writeJsonString(R, bool pretty = false)(ref R dst, in Json json, size_t level = 0)
1739 //	if( isOutputRange!R && is(ElementEncodingType!R == char) )
1740 {
1741 	final switch( json.type ){
1742 		case Json.Type.undefined: dst.put("undefined"); break;
1743 		case Json.Type.null_: dst.put("null"); break;
1744 		case Json.Type.bool_: dst.put(cast(bool)json ? "true" : "false"); break;
1745 		case Json.Type.int_: formattedWrite(dst, "%d", json.get!long); break;
1746 		case Json.Type.float_:
1747 			auto d = json.get!double;
1748 			if (d != d)
1749 				dst.put("undefined"); // JSON has no NaN value so set null
1750 			else
1751 				formattedWrite(dst, "%.16g", json.get!double);
1752 			break;
1753 		case Json.Type..string:
1754 			dst.put('\"');
1755 			jsonEscape(dst, cast(string)json);
1756 			dst.put('\"');
1757 			break;
1758 		case Json.Type.array:
1759 			dst.put('[');
1760 			bool first = true;
1761 			foreach (ref const Json e; json) {
1762 				if( !first ) dst.put(",");
1763 				first = false;
1764 				static if (pretty) {
1765 					dst.put('\n');
1766 					foreach (tab; 0 .. level+1) dst.put('\t');
1767 				}
1768 				if (e.type == Json.Type.undefined) dst.put("null");
1769 				else writeJsonString!(R, pretty)(dst, e, level+1);
1770 			}
1771 			static if (pretty) {
1772 				if (json.length > 0) {
1773 					dst.put('\n');
1774 					foreach (tab; 0 .. level) dst.put('\t');
1775 				}
1776 			}
1777 			dst.put(']');
1778 			break;
1779 		case Json.Type.object:
1780 			dst.put('{');
1781 			bool first = true;
1782 
1783 			static if (pretty) {
1784 				import std.algorithm.sorting : sort;
1785 				string[] keyOrder;
1786 				foreach (string key, ref const Json e; json) keyOrder ~= key;
1787 				keyOrder.sort();
1788 
1789 				foreach( key; keyOrder ){
1790 					if( json[key].type == Json.Type.undefined ) continue;
1791 					if( !first ) dst.put(',');
1792 					first = false;
1793 					dst.put('\n');
1794 					foreach (tab; 0 .. level+1) dst.put('\t');
1795 					dst.put('\"');
1796 					jsonEscape(dst, key);
1797 					dst.put(pretty ? `": ` : `":`);
1798 					writeJsonString!(R, pretty)(dst, json[key], level+1);
1799 				}
1800 				if (json.length > 0) {
1801 					dst.put('\n');
1802 					foreach (tab; 0 .. level) dst.put('\t');
1803 				}
1804 			} else {
1805 				foreach( string k, ref const Json e; json ){
1806 					if( e.type == Json.Type.undefined ) continue;
1807 					if( !first ) dst.put(',');
1808 					first = false;
1809 					dst.put('\"');
1810 					jsonEscape(dst, k);
1811 					dst.put(pretty ? `": ` : `":`);
1812 					writeJsonString!(R, pretty)(dst, e, level+1);
1813 				}
1814 			}
1815 			dst.put('}');
1816 			break;
1817 	}
1818 }
1819 
1820 unittest {
1821 	auto a = Json.emptyObject;
1822 	a["a"] = Json.emptyArray;
1823 	a["b"] = Json.emptyArray;
1824 	a["b"] ~= Json(1);
1825 	a["b"] ~= Json.emptyObject;
1826 
1827 	assert(a.toString() == `{"a":[],"b":[1,{}]}` || a.toString == `{"b":[1,{}],"a":[]}`);
1828 	assert(a.toPrettyString() ==
1829 `{
1830 	"a": [],
1831 	"b": [
1832 		1,
1833 		{}
1834 	]
1835 }`);
1836 }
1837 
1838 unittest { // #735
1839 	auto a = Json.emptyArray;
1840 	a ~= "a";
1841 	a ~= Json();
1842 	a ~= "b";
1843 	a ~= null;
1844 	a ~= "c";
1845 	assert(a.toString() == `["a",null,"b",null,"c"]`);
1846 }
1847 
1848 unittest {
1849 	auto a = Json.emptyArray;
1850 	a ~= Json(1);
1851 	a ~= Json(2);
1852 	a ~= Json(3);
1853 	a ~= Json(4);
1854 	a ~= Json(5);
1855 
1856 	auto b = Json(a[0..a.length]);
1857 	assert(a == b);
1858 
1859 	auto c = Json(a[0..$]);
1860 	assert(a == c);
1861 	assert(b == c);
1862 
1863 	auto d = [Json(1),Json(2),Json(3)];
1864 	assert(d == a[0..a.length-2]);
1865 	assert(d == a[0..$-2]);
1866 }
1867 
1868 unittest {
1869 	auto j = Json(double.init);
1870 
1871 	assert(j.toString == "undefined"); // A double nan should serialize to undefined
1872 	j = 17.04f;
1873 	assert(j.toString == "17.04");	// A proper double should serialize correctly
1874 
1875 	double d;
1876 	deserializeJson(d, Json.undefined); // Json.undefined should deserialize to nan
1877 	assert(d != d);
1878 }
1879 /**
1880 	Writes the given JSON object as a prettified JSON string into the destination range.
1881 
1882 	The output will contain newlines and indents to make the output human readable.
1883 
1884 	Params:
1885 		dst   = References the string output range to which the result is written.
1886 		json  = Specifies the JSON value that is to be stringified.
1887 		level = Specifies the base amount of indentation for the output. Indentation  is always
1888 		        done using tab characters.
1889 
1890 	See_Also: Json.toPrettyString, writeJsonString
1891 */
1892 void writePrettyJsonString(R)(ref R dst, in Json json, int level = 0)
1893 //	if( isOutputRange!R && is(ElementEncodingType!R == char) )
1894 {
1895 	writeJsonString!(R, true)(dst, json, level);
1896 }
1897 
1898 
1899 /**
1900 	Helper function that escapes all Unicode characters in a JSON string.
1901 */
1902 string convertJsonToASCII(string json)
1903 {
1904 	auto ret = appender!string;
1905 	jsonEscape!true(ret, json);
1906 	return ret.data;
1907 }
1908 
1909 
1910 /// private
1911 private void jsonEscape(bool escape_unicode = false, R)(ref R dst, string s)
1912 {
1913 	for (size_t pos = 0; pos < s.length; pos++) {
1914 		immutable(char) ch = s[pos];
1915 
1916 		switch (ch) {
1917 			default:
1918 				static if (escape_unicode) {
1919 					if (ch > 0x20 && ch < 0x80) dst.put(ch);
1920 					else {
1921 						import std.utf : decode;
1922 						char[13] buf;
1923 						int len;
1924 						dchar codepoint = decode(s, pos);
1925 						import core.stdc.stdio : sprintf;
1926 						/* codepoint is in BMP */
1927 						if(codepoint < 0x10000)
1928 						{
1929 							sprintf(&buf[0], "\\u%04X", codepoint);
1930 							len = 6;
1931 						}
1932 						/* not in BMP -> construct a UTF-16 surrogate pair */
1933 						else
1934 						{
1935 							int first, last;
1936 
1937 							codepoint -= 0x10000;
1938 							first = 0xD800 | ((codepoint & 0xffc00) >> 10);
1939 							last = 0xDC00 | (codepoint & 0x003ff);
1940 
1941 							sprintf(&buf[0], "\\u%04X\\u%04X", first, last);
1942 							len = 12;
1943 						}
1944 
1945 						pos -= 1;
1946 						foreach (i; 0 .. len)
1947 							dst.put(buf[i]);
1948 
1949 					}
1950 				} else {
1951 					if (ch < 0x20) dst.formattedWrite("\\u%04X", ch);
1952 					else dst.put(ch);
1953 				}
1954 				break;
1955 			case '\\': dst.put("\\\\"); break;
1956 			case '\r': dst.put("\\r"); break;
1957 			case '\n': dst.put("\\n"); break;
1958 			case '\t': dst.put("\\t"); break;
1959 			case '\"': dst.put("\\\""); break;
1960 		}
1961 	}
1962 }
1963 
1964 /// private
1965 private string jsonUnescape(R)(ref R range, string filename, int* line)
1966 {
1967 	auto ret = appender!string();
1968 	while(!range.empty){
1969 		auto ch = range.front;
1970 		switch( ch ){
1971 			case '"': return ret.data;
1972 			case '\\':
1973 				range.popFront();
1974 				enforceJson(!range.empty, "Unterminated string escape sequence.", filename, line);
1975 				switch(range.front){
1976 					default: enforceJson(false, "Invalid string escape sequence.", filename, line); break;
1977 					case '"': ret.put('\"'); range.popFront(); break;
1978 					case '\\': ret.put('\\'); range.popFront(); break;
1979 					case '/': ret.put('/'); range.popFront(); break;
1980 					case 'b': ret.put('\b'); range.popFront(); break;
1981 					case 'f': ret.put('\f'); range.popFront(); break;
1982 					case 'n': ret.put('\n'); range.popFront(); break;
1983 					case 'r': ret.put('\r'); range.popFront(); break;
1984 					case 't': ret.put('\t'); range.popFront(); break;
1985 					case 'u':
1986 
1987 						dchar decode_unicode_escape() {
1988 							enforceJson(range.front == 'u');
1989 							range.popFront();
1990 							dchar uch = 0;
1991 							foreach( i; 0 .. 4 ){
1992 								uch *= 16;
1993 								enforceJson(!range.empty, "Unicode sequence must be '\\uXXXX'.", filename, line);
1994 								auto dc = range.front;
1995 								range.popFront();
1996 
1997 								if( dc >= '0' && dc <= '9' ) uch += dc - '0';
1998 								else if( dc >= 'a' && dc <= 'f' ) uch += dc - 'a' + 10;
1999 								else if( dc >= 'A' && dc <= 'F' ) uch += dc - 'A' + 10;
2000 								else enforceJson(false, "Unicode sequence must be '\\uXXXX'.", filename, line);
2001 							}
2002 							return uch;
2003 						}
2004 
2005 						auto uch = decode_unicode_escape();
2006 
2007 						if(0xD800 <= uch && uch <= 0xDBFF) {
2008 							/* surrogate pair */
2009 							range.popFront(); // backslash '\'
2010 							auto uch2 = decode_unicode_escape();
2011 							enforceJson(0xDC00 <= uch2 && uch2 <= 0xDFFF, "invalid Unicode", filename, line);
2012 							{
2013 								/* valid second surrogate */
2014 								uch =
2015 									((uch - 0xD800) << 10) +
2016 										(uch2 - 0xDC00) +
2017 										0x10000;
2018 							}
2019 						}
2020 						ret.put(uch);
2021 						break;
2022 				}
2023 				break;
2024 			default:
2025 				ret.put(ch);
2026 				range.popFront();
2027 				break;
2028 		}
2029 	}
2030 	return ret.data;
2031 }
2032 
2033 /// private
2034 private string skipNumber(R)(ref R s, out bool is_float, string filename, int* line)
2035 {
2036 	// TODO: make this work with input ranges
2037 	size_t idx = 0;
2038 	is_float = false;
2039 	if (s[idx] == '-') idx++;
2040 	if (s[idx] == '0') idx++;
2041 	else {
2042 		enforceJson(isDigit(s[idx++]), "Digit expected at beginning of number.", filename, line);
2043 		while( idx < s.length && isDigit(s[idx]) ) idx++;
2044 	}
2045 
2046 	if( idx < s.length && s[idx] == '.' ){
2047 		idx++;
2048 		is_float = true;
2049 		while( idx < s.length && isDigit(s[idx]) ) idx++;
2050 	}
2051 
2052 	if( idx < s.length && (s[idx] == 'e' || s[idx] == 'E') ){
2053 		idx++;
2054 		is_float = true;
2055 		if( idx < s.length && (s[idx] == '+' || s[idx] == '-') ) idx++;
2056 		enforceJson( idx < s.length && isDigit(s[idx]), "Expected exponent." ~ s[0 .. idx], filename, line);
2057 		idx++;
2058 		while( idx < s.length && isDigit(s[idx]) ) idx++;
2059 	}
2060 
2061 	string ret = s[0 .. idx];
2062 	s = s[idx .. $];
2063 	return ret;
2064 }
2065 
2066 /// private
2067 private string skipJsonString(R)(ref R s, string filename, int* line)
2068 {
2069 	// TODO: count or disallow any newlines inside of the string
2070 	enforceJson(!s.empty && s.front == '"', "Expected '\"' to start string.", filename, line);
2071 	s.popFront();
2072 	string ret = jsonUnescape(s, filename, line);
2073 	enforceJson(!s.empty && s.front == '"', "Expected '\"' to terminate string.", filename, line);
2074 	s.popFront();
2075 	return ret;
2076 }
2077 
2078 /// private
2079 private void skipWhitespace(R)(ref R s, int* line = null)
2080 {
2081 	while (!s.empty) {
2082 		switch (s.front) {
2083 			default: return;
2084 			case ' ', '\t': s.popFront(); break;
2085 			case '\n':
2086 				s.popFront();
2087 				if (!s.empty && s.front == '\r') s.popFront();
2088 				if (line) (*line)++;
2089 				break;
2090 			case '\r':
2091 				s.popFront();
2092 				if (!s.empty && s.front == '\n') s.popFront();
2093 				if (line) (*line)++;
2094 				break;
2095 		}
2096 	}
2097 }
2098 
2099 private bool isDigit(dchar ch) { return ch >= '0' && ch <= '9'; }
2100 
2101 private string underscoreStrip(string field_name)
2102 {
2103 	if( field_name.length < 1 || field_name[$-1] != '_' ) return field_name;
2104 	else return field_name[0 .. $-1];
2105 }
2106 
2107 /// private
2108 package template isJsonSerializable(T) { enum isJsonSerializable = is(typeof(T.init.toJson()) == Json) && is(typeof(T.fromJson(Json())) == T); }
2109 
2110 private void enforceJson(string file = __FILE__, size_t line = __LINE__)(bool cond, lazy string message = "JSON exception")
2111 {
2112 	static if (__VERSION__ >= 2079)
2113 		enforce!JSONException(cond, message, file, line);
2114 	else
2115 		enforceEx!JSONException(cond, message, file, line);
2116 }
2117 
2118 private void enforceJson(string file = __FILE__, size_t line = __LINE__)(bool cond, lazy string message, string err_file, int err_line)
2119 {
2120 	auto errmsg() { return format("%s(%s): Error: %s", err_file, err_line+1, message); }
2121 	static if (__VERSION__ >= 2079)
2122 		enforce!JSONException(cond, errmsg, file, line);
2123 	else
2124 		enforceEx!JSONException(cond, errmsg, file, line);
2125 }
2126 
2127 private void enforceJson(string file = __FILE__, size_t line = __LINE__)(bool cond, lazy string message, string err_file, int* err_line)
2128 {
2129 	enforceJson!(file, line)(cond, message, err_file, err_line ? *err_line : -1);
2130 }