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