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