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