1 module dub.internal.vibecompat.data.json;
2 
3 version (Have_vibe_d) public import vibe.data.json;
4 else:
5 
6 import dub.internal.vibecompat.data.utils;
7 
8 import std.array;
9 import std.conv;
10 import std.datetime;
11 import std.exception;
12 import std.format;
13 import std.string;
14 import std.range;
15 import std.traits;
16 
17 version = JsonLineNumbers;
18 
19 
20 /******************************************************************************/
21 /* public types                                                               */
22 /******************************************************************************/
23 
24 /**
25 	Represents a single JSON value.
26 
27 	Json values can have one of the types defined in the Json.Type enum. They
28 	behave mostly like values in ECMA script in the way that you can
29 	transparently perform operations on them. However, strict typechecking is
30 	done, so that operations between differently typed JSON values will throw
31 	an exception. Additionally, an explicit cast or using get!() or to!() is
32 	required to convert a JSON value to the corresponding static D type.
33 */
34 struct Json {
35 	private {
36 		union {
37 			bool m_bool;
38 			long m_int;
39 			double m_float;
40 			string m_string;
41 			Json[] m_array;
42 			Json[string] m_object;
43 		};
44 		Type m_type = Type.undefined;
45 		uint m_magic = 0x1337f00d; // workaround for Appender bug
46 		string m_name;
47 	}
48 
49 	/** Represents the run time type of a JSON value.
50 	*/
51 	enum Type {
52 		/// A non-existent value in a JSON object
53 		undefined,
54 		/// Null value
55 		null_,
56 		/// Boolean value
57 		bool_,
58 		/// 64-bit integer value
59 		int_,
60 		/// 64-bit floating point value
61 		float_,
62 		/// UTF-8 string
63 		string,
64 		/// Array of JSON values
65 		array,
66 		/// JSON object aka. dictionary from string to Json
67 		object
68 	}
69 
70 	/// New JSON value of Type.undefined
71 	static @property Json undefined() { return Json(); }
72 
73 	/// New JSON value of Type.object
74 	static @property Json emptyObject() { return Json(cast(Json[string])null); }
75 
76 	/// New JSON value of Type.array
77 	static @property Json emptyArray() { return Json(cast(Json[])null); }
78 
79 	version(JsonLineNumbers) int line;
80 
81 	/**
82 		Constructor for a JSON object.
83 	*/
84 	this(typeof(null)) { m_type = Type.null_; }
85 	/// ditto
86 	this(bool v) { m_type = Type.bool_; m_bool = v; }
87 	/// ditto
88 	this(int v) { m_type = Type.int_; m_int = v; }
89 	/// ditto
90 	this(long v) { m_type = Type.int_; m_int = v; }
91 	/// ditto
92 	this(double v) { m_type = Type.float_; m_float = v; }
93 	/// ditto
94 	this(string v) { m_type = Type..string; m_string = v; }
95 	/// ditto
96 	this(Json[] v) { m_type = Type.array; m_array = v; }
97 	/// ditto
98 	this(Json[string] v) { m_type = Type.object; m_object = v; }
99 
100 	/**
101 		Allows assignment of D values to a JSON value.
102 	*/
103 	ref Json opAssign(Json v){
104 		m_type = v.m_type;
105 		final switch(m_type){
106 			case Type.undefined: m_string = null; break;
107 			case Type.null_: m_string = null; break;
108 			case Type.bool_: m_bool = v.m_bool; break;
109 			case Type.int_: m_int = v.m_int; break;
110 			case Type.float_: m_float = v.m_float; break;
111 			case Type..string: m_string = v.m_string; break;
112 			case Type.array:
113 				m_array = v.m_array;
114 				if (m_magic == 0x1337f00d) { foreach (ref av; m_array) av.m_name = m_name; } else m_name = null;
115 				break;
116 			case Type.object:
117 				m_object = v.m_object;
118 				if (m_magic == 0x1337f00d) { foreach (k, ref av; m_object) av.m_name = m_name ~ "." ~ k; } else m_name = null;
119 				break;
120 		}
121 		return this;
122 	}
123 	/// ditto
124 	void opAssign(typeof(null)) { m_type = Type.null_; m_string = null; }
125 	/// ditto
126 	bool opAssign(bool v) { m_type = Type.bool_; m_bool = v; return v; }
127 	/// ditto
128 	int opAssign(int v) { m_type = Type.int_; m_int = v; return v; }
129 	/// ditto
130 	long opAssign(long v) { m_type = Type.int_; m_int = v; return v; }
131 	/// ditto
132 	double opAssign(double v) { m_type = Type.float_; m_float = v; return v; }
133 	/// ditto
134 	string opAssign(string v) { m_type = Type..string; m_string = v; return v; }
135 	/// ditto
136 	Json[] opAssign(Json[] v)
137 	{
138 		m_type = Type.array;
139 		m_array = v;
140 		if (m_magic == 0x1337f00d) foreach (ref av; m_array) av.m_name = m_name;
141 		return v;
142 	}
143 	/// ditto
144 	Json[string] opAssign(Json[string] v)
145 	{
146 		m_type = Type.object;
147 		m_object = v;
148 		if (m_magic == 0x1337f00d) foreach (k, ref av; m_object) av.m_name = m_name ~ "." ~ k;
149 		return v;
150 	}
151 
152 	/**
153 		The current type id of this JSON object.
154 	*/
155 	@property Type type() const { return m_type; }
156 
157 	/**
158 		Allows direct indexing of array typed JSON values.
159 	*/
160 	ref inout(Json) opIndex(size_t idx) inout { checkType!(Json[])(); return m_array[idx]; }
161 
162 	/**
163 		Allows direct indexing of object typed JSON values using a string as
164 		the key.
165 	*/
166 	const(Json) opIndex(string key) const {
167 		checkType!(Json[string])();
168 		if( auto pv = key in m_object ) return *pv;
169 		Json ret = Json.undefined;
170 		ret.m_string = key;
171 		return ret;
172 	}
173 	/// ditto
174 	ref Json opIndex(string key){
175 		checkType!(Json[string])();
176 		if( auto pv = key in m_object )
177 			return *pv;
178 		m_object[key] = Json();
179 		m_object[key].m_type = Type.undefined; // DMDBUG: AAs are teh $H1T!!!11
180 		assert(m_object[key].type == Type.undefined);
181 		m_object[key].m_name = m_name ~ "." ~ key;
182 		m_object[key].m_string = key;
183 		return m_object[key];
184 	}
185 
186 	/**
187 		Returns a slice of a JSON array.
188 	*/
189 	inout(Json[]) opSlice() inout { checkType!(Json[])(); return m_array; }
190 	///
191 	inout(Json[]) opSlice(size_t from, size_t to) inout { checkType!(Json[])(); return m_array[from .. to]; }
192 
193 	/**
194 		Removes an entry from an object.
195 	*/
196 	void remove(string item) { checkType!(Json[string])(); m_object.remove(item); }
197 
198 	/**
199 		Returns the number of entries of string, array or object typed JSON values.
200 	*/
201 	@property size_t length()
202 	const {
203 		checkType!(string, Json[], Json[string]);
204 		switch(m_type){
205 			case Type..string: return m_string.length;
206 			case Type.array: return m_array.length;
207 			case Type.object: return m_object.length;
208 			default: assert(false);
209 		}
210 	}
211 
212 	/**
213 		Allows foreach iterating over JSON objects and arrays.
214 	*/
215 	int opApply(int delegate(ref Json obj) del)
216 	{
217 		checkType!(Json[], Json[string]);
218 		if( m_type == Type.array ){
219 			foreach( ref v; m_array )
220 				if( auto ret = del(v) )
221 					return ret;
222 			return 0;
223 		} else {
224 			foreach( ref v; m_object )
225 				if( v.type != Type.undefined )
226 					if( auto ret = del(v) )
227 						return ret;
228 			return 0;
229 		}
230 	}
231 	/// ditto
232 	int opApply(int delegate(ref const Json obj) del)
233 	const {
234 		checkType!(Json[], Json[string]);
235 		if( m_type == Type.array ){
236 			foreach( ref v; m_array )
237 				if( auto ret = del(v) )
238 					return ret;
239 			return 0;
240 		} else {
241 			foreach( ref v; m_object )
242 				if( v.type != Type.undefined )
243 					if( auto ret = del(v) )
244 						return ret;
245 			return 0;
246 		}
247 	}
248 	/// ditto
249 	int opApply(int delegate(ref size_t idx, ref Json obj) del)
250 	{
251 		checkType!(Json[]);
252 		foreach( idx, ref v; m_array )
253 			if( auto ret = del(idx, v) )
254 				return ret;
255 		return 0;
256 	}
257 	/// ditto
258 	int opApply(int delegate(ref size_t idx, ref const Json obj) del)
259 	const {
260 		checkType!(Json[]);
261 		foreach( idx, ref v; m_array )
262 			if( auto ret = del(idx, v) )
263 				return ret;
264 		return 0;
265 	}
266 	/// ditto
267 	int opApply(int delegate(ref string idx, ref Json obj) del)
268 	{
269 		checkType!(Json[string]);
270 		foreach( idx, ref v; m_object )
271 			if( v.type != Type.undefined )
272 				if( auto ret = del(idx, v) )
273 					return ret;
274 		return 0;
275 	}
276 	/// ditto
277 	int opApply(int delegate(ref string idx, ref const Json obj) del)
278 	const {
279 		checkType!(Json[string]);
280 		foreach( idx, ref v; m_object )
281 			if( v.type != Type.undefined )
282 				if( auto ret = del(idx, v) )
283 					return ret;
284 		return 0;
285 	}
286 
287 	/**
288 		Converts the JSON value to the corresponding D type - types must match exactly.
289 	*/
290 	inout(T) opCast(T)() inout { return get!T; }
291 	/// ditto
292 	@property inout(T) get(T)()
293 	inout {
294 		checkType!T();
295 		static if( is(T == bool) ) return m_bool;
296 		else static if( is(T == double) ) return m_float;
297 		else static if( is(T == float) ) return cast(T)m_float;
298 		else static if( is(T == long) ) return m_int;
299 		else static if( is(T : long) ){ enforce(m_int <= T.max && m_int >= T.min); return cast(T)m_int; }
300 		else static if( is(T == string) ) return m_string;
301 		else static if( is(T == Json[]) ) return m_array;
302 		else static if( is(T == Json[string]) ) return m_object;
303 		else static assert("JSON can only be casted to (bool, long, double, string, Json[] or Json[string]. Not "~T.stringof~".");
304 	}
305 	/// ditto
306 	@property const(T) opt(T)(const(T) def = T.init)
307 	const {
308 		if( typeId!T != m_type ) return def;
309 		return get!T;
310 	}
311 	/// ditto
312 	@property T opt(T)(T def = T.init)
313 	{
314 		if( typeId!T != m_type ) return def;
315 		return get!T;
316 	}
317 
318 	/**
319 		Converts the JSON value to the corresponding D type - types are converted as neccessary.
320 	*/
321 	@property inout(T) to(T)()
322 	inout {
323 		static if( is(T == bool) ){
324 			final switch( m_type ){
325 				case Type.undefined: return false;
326 				case Type.null_: return false;
327 				case Type.bool_: return m_bool;
328 				case Type.int_: return m_int != 0;
329 				case Type.float_: return m_float != 0;
330 				case Type..string: return m_string.length > 0;
331 				case Type.array: return m_array.length > 0;
332 				case Type.object: return m_object.length > 0;
333 			}
334 		} else static if( is(T == double) ){
335 			final switch( m_type ){
336 				case Type.undefined: return T.init;
337 				case Type.null_: return 0;
338 				case Type.bool_: return m_bool ? 1 : 0;
339 				case Type.int_: return m_int;
340 				case Type.float_: return m_float;
341 				case Type..string: return .to!double(cast(string)m_string);
342 				case Type.array: return double.init;
343 				case Type.object: return double.init;
344 			}
345 		} else static if( is(T == float) ){
346 			final switch( m_type ){
347 				case Type.undefined: return T.init;
348 				case Type.null_: return 0;
349 				case Type.bool_: return m_bool ? 1 : 0;
350 				case Type.int_: return m_int;
351 				case Type.float_: return m_float;
352 				case Type..string: return .to!float(cast(string)m_string);
353 				case Type.array: return float.init;
354 				case Type.object: return float.init;
355 			}
356 		}
357 		else static if( is(T == long) ){
358 			final switch( m_type ){
359 				case Type.undefined: return 0;
360 				case Type.null_: return 0;
361 				case Type.bool_: return m_bool ? 1 : 0;
362 				case Type.int_: return m_int;
363 				case Type.float_: return cast(long)m_float;
364 				case Type..string: return .to!long(m_string);
365 				case Type.array: return 0;
366 				case Type.object: return 0;
367 			}
368 		} else static if( is(T : long) ){
369 			final switch( m_type ){
370 				case Type.undefined: return 0;
371 				case Type.null_: return 0;
372 				case Type.bool_: return m_bool ? 1 : 0;
373 				case Type.int_: return cast(T)m_int;
374 				case Type.float_: return cast(T)m_float;
375 				case Type..string: return cast(T).to!long(cast(string)m_string);
376 				case Type.array: return 0;
377 				case Type.object: return 0;
378 			}
379 		} else static if( is(T == string) ){
380 			switch( m_type ){
381 				default: return toString();
382 				case Type..string: return m_string;
383 			}
384 		} else static if( is(T == Json[]) ){
385 			switch( m_type ){
386 				default: return Json([this]);
387 				case Type.array: return m_array;
388 			}
389 		} else static if( is(T == Json[string]) ){
390 			switch( m_type ){
391 				default: return Json(["value": this]);
392 				case Type.object: return m_object;
393 			}
394 		} else static assert("JSON can only be casted to (bool, long, double, string, Json[] or Json[string]. Not "~T.stringof~".");
395 	}
396 
397 	/**
398 		Performs unary operations on the JSON value.
399 
400 		The following operations are supported for each type:
401 
402 		$(DL
403 			$(DT Null)   $(DD none)
404 			$(DT Bool)   $(DD ~)
405 			$(DT Int)    $(DD +, -, ++, --)
406 			$(DT Float)  $(DD +, -, ++, --)
407 			$(DT String) $(DD none)
408 			$(DT Array)  $(DD none)
409 			$(DT Object) $(DD none)
410 		)
411 	*/
412 	Json opUnary(string op)()
413 	const {
414 		static if( op == "~" ){
415 			checkType!bool();
416 			return Json(~m_bool);
417 		} else static if( op == "+" || op == "-" || op == "++" || op == "--" ){
418 			checkType!(long, double);
419 			if( m_type == Type.int_ ) mixin("return Json("~op~"m_int);");
420 			else if( m_type == Type.float_ ) mixin("return Json("~op~"m_float);");
421 			else assert(false);
422 		} else static assert("Unsupported operator '"~op~"' for type JSON.");
423 	}
424 
425 	/**
426 		Performs binary operations between JSON values.
427 
428 		The two JSON values must be of the same run time type or an exception
429 		will be thrown. Only the operations listed are allowed for each of the
430 		types.
431 
432 		$(DL
433 			$(DT Null)   $(DD none)
434 			$(DT Bool)   $(DD &&, ||)
435 			$(DT Int)    $(DD +, -, *, /, %)
436 			$(DT Float)  $(DD +, -, *, /, %)
437 			$(DT String) $(DD ~)
438 			$(DT Array)  $(DD ~)
439 			$(DT Object) $(DD none)
440 		)
441 	*/
442 	Json opBinary(string op)(ref const(Json) other)
443 	const {
444 		enforce(m_type == other.m_type, "Binary operation '"~op~"' between "~.to!string(m_type)~" and "~.to!string(other.m_type)~" JSON objects.");
445 		static if( op == "&&" ){
446 			checkType!(bool)("'&&'"); other.checkType!(bool)("'&&'");
447 			return Json(m_bool && other.m_bool);
448 		} else static if( op == "||" ){
449 			checkType!(bool)("'||'"); other.checkType!(bool)("'||'");
450 			return Json(m_bool || other.m_bool);
451 		} else static if( op == "+" ){
452 			checkType!(double, long)("'+'"); other.checkType!(double, long)("'+'");
453 			if( m_type == Type.int_ ) return Json(m_int + other.m_int);
454 			else if( m_type == Type.float_ ) return Json(m_float + other.m_float);
455 			else assert(false);
456 		} else static if( op == "-" ){
457 			checkType!(double, long)("'-'"); other.checkType!(double, long)("'-'");
458 			if( m_type == Type.int_ ) return Json(m_int - other.m_int);
459 			else if( m_type == Type.float_ ) return Json(m_float - other.m_float);
460 			else assert(false);
461 		} else static if( op == "*" ){
462 			checkType!(double, long)("'*'"); other.checkType!(double, long)("'*'");
463 			if( m_type == Type.int_ ) return Json(m_int * other.m_int);
464 			else if( m_type == Type.float_ ) return Json(m_float * other.m_float);
465 			else assert(false);
466 		} else static if( op == "/" ){
467 			checkType!(double, long)("'/'"); other.checkType!(double, long)("'/'");
468 			if( m_type == Type.int_ ) return Json(m_int / other.m_int);
469 			else if( m_type == Type.float_ ) return Json(m_float / other.m_float);
470 			else assert(false);
471 		} else static if( op == "%" ){
472 			checkType!(double, long)("'%'"); other.checkType!(double, long)("'%'");
473 			if( m_type == Type.int_ ) return Json(m_int % other.m_int);
474 			else if( m_type == Type.float_ ) return Json(m_float % other.m_float);
475 			else assert(false);
476 		} else static if( op == "~" ){
477 			checkType!(string)("'~'"); other.checkType!(string)("'~'");
478 			if( m_type == Type..string ) return Json(m_string ~ other.m_string);
479 			else assert(false);
480 		} else static assert("Unsupported operator '"~op~"' for type JSON.");
481 	}
482 	/// ditto
483 	Json opBinary(string op)(Json other)
484 		if( op == "~" )
485 	{
486 		static if( op == "~" ){
487 			checkType!(string, Json[])("'~'"); other.checkType!(string, Json[])("'~'");
488 			if( m_type == Type..string ) return Json(m_string ~ other.m_string);
489 			else if( m_type == Type.array ) return Json(m_array ~ other.m_array);
490 			else assert(false);
491 		} else static assert("Unsupported operator '"~op~"' for type JSON.");
492 	}
493 	/// ditto
494 	void opOpAssign(string op)(Json other)
495 		if( op == "+" || op == "-" || op == "*" ||op == "/" || op == "%" )
496 	{
497 		enforce(m_type == other.m_type, "Binary operation '"~op~"' between "~.to!string(m_type)~" and "~.to!string(other.m_type)~" JSON objects.");
498 		static if( op == "+" ){
499 			if( m_type == Type.int_ ) m_int += other.m_int;
500 			else if( m_type == Type.float_ ) m_float += other.m_float;
501 			else enforce(false, "'+' only allowed for scalar types, not "~.to!string(m_type)~".");
502 		} else static if( op == "-" ){
503 			if( m_type == Type.int_ ) m_int -= other.m_int;
504 			else if( m_type == Type.float_ ) m_float -= other.m_float;
505 			else enforce(false, "'-' only allowed for scalar types, not "~.to!string(m_type)~".");
506 		} else static if( op == "*" ){
507 			if( m_type == Type.int_ ) m_int *= other.m_int;
508 			else if( m_type == Type.float_ ) m_float *= other.m_float;
509 			else enforce(false, "'*' only allowed for scalar types, not "~.to!string(m_type)~".");
510 		} else static if( op == "/" ){
511 			if( m_type == Type.int_ ) m_int /= other.m_int;
512 			else if( m_type == Type.float_ ) m_float /= other.m_float;
513 			else enforce(false, "'/' only allowed for scalar types, not "~.to!string(m_type)~".");
514 		} else static if( op == "%" ){
515 			if( m_type == Type.int_ ) m_int %= other.m_int;
516 			else if( m_type == Type.float_ ) m_float %= other.m_float;
517 			else enforce(false, "'%' only allowed for scalar types, not "~.to!string(m_type)~".");
518 		} /*else static if( op == "~" ){
519 			if( m_type == Type.string ) m_string ~= other.m_string;
520 			else if( m_type == Type.array ) m_array ~= other.m_array;
521 			else enforce(false, "'%' only allowed for scalar types, not "~.to!string(m_type)~".");
522 		}*/ else static assert("Unsupported operator '"~op~"' for type JSON.");
523 		assert(false);
524 	}
525 	/// ditto
526 	Json opBinary(string op)(bool other) const { checkType!bool(); mixin("return Json(m_bool "~op~" other);"); }
527 	/// ditto
528 	Json opBinary(string op)(long other) const { checkType!long(); mixin("return Json(m_int "~op~" other);"); }
529 	/// ditto
530 	Json opBinary(string op)(double other) const { checkType!double(); mixin("return Json(m_float "~op~" other);"); }
531 	/// ditto
532 	Json opBinary(string op)(string other) const { checkType!string(); mixin("return Json(m_string "~op~" other);"); }
533 	/// ditto
534 	Json opBinary(string op)(Json other)
535 	if (op == "~") {
536 		if (m_type == Type.array) return Json(m_array ~ other);
537 		else return Json(this ~ other);
538 	}
539 	/// ditto
540 	Json opBinaryRight(string op)(bool other) const { checkType!bool(); mixin("return Json(other "~op~" m_bool);"); }
541 	/// ditto
542 	Json opBinaryRight(string op)(long other) const { checkType!long(); mixin("return Json(other "~op~" m_int);"); }
543 	/// ditto
544 	Json opBinaryRight(string op)(double other) const { checkType!double(); mixin("return Json(other "~op~" m_float);"); }
545 	/// ditto
546 	Json opBinaryRight(string op)(string other) const if(op == "~") { checkType!string(); return Json(other ~ m_string); }
547 	/// ditto
548 	inout(Json)* opBinaryRight(string op)(string other) inout if(op == "in") {
549 		checkType!(Json[string])();
550 		auto pv = other in m_object;
551 		if( !pv ) return null;
552 		if( pv.type == Type.undefined ) return null;
553 		return pv;
554 	}
555 
556 	/**
557 		Allows to access existing fields of a JSON object using dot syntax.
558 	*/
559 	@property const(Json) opDispatch(string prop)() const { return opIndex(prop); }
560 	/// ditto
561 	@property ref Json opDispatch(string prop)() { return opIndex(prop); }
562 
563 	/**
564 		Compares two JSON values for equality.
565 
566 		If the two values have different types, they are considered unequal.
567 		This differs with ECMA script, which performs a type conversion before
568 		comparing the values.
569 	*/
570 	bool opEquals(ref const Json other)
571 	const {
572 		if( m_type != other.m_type ) return false;
573 		final switch(m_type){
574 			case Type.undefined: return false;
575 			case Type.null_: return true;
576 			case Type.bool_: return m_bool == other.m_bool;
577 			case Type.int_: return m_int == other.m_int;
578 			case Type.float_: return m_float == other.m_float;
579 			case Type..string: return m_string == other.m_string;
580 			case Type.array: return m_array == other.m_array;
581 			case Type.object: return m_object == other.m_object;
582 		}
583 	}
584 	/// ditto
585 	bool opEquals(const Json other) const { return opEquals(other); }
586 	/// ditto
587 	bool opEquals(typeof(null)) const { return m_type == Type.null_; }
588 	/// ditto
589 	bool opEquals(bool v) const { return m_type == Type.bool_ && m_bool == v; }
590 	/// ditto
591 	bool opEquals(long v) const { return m_type == Type.int_ && m_int == v; }
592 	/// ditto
593 	bool opEquals(double v) const { return m_type == Type.float_ && m_float == v; }
594 	/// ditto
595 	bool opEquals(string v) const { return m_type == Type..string && m_string == v; }
596 
597 	/**
598 		Compares two JSON values.
599 
600 		If the types of the two values differ, the value with the smaller type
601 		id is considered the smaller value. This differs from ECMA script, which
602 		performs a type conversion before comparing the values.
603 
604 		JSON values of type Object cannot be compared and will throw an
605 		exception.
606 	*/
607 	int opCmp(ref const Json other)
608 	const {
609 		if( m_type != other.m_type ) return m_type < other.m_type ? -1 : 1;
610 		final switch(m_type){
611 			case Type.undefined: return 0;
612 			case Type.null_: return 0;
613 			case Type.bool_: return m_bool < other.m_bool ? -1 : m_bool == other.m_bool ? 0 : 1;
614 			case Type.int_: return m_int < other.m_int ? -1 : m_int == other.m_int ? 0 : 1;
615 			case Type.float_: return m_float < other.m_float ? -1 : m_float == other.m_float ? 0 : 1;
616 			case Type..string: return m_string < other.m_string ? -1 : m_string == other.m_string ? 0 : 1;
617 			case Type.array: return m_array < other.m_array ? -1 : m_array == other.m_array ? 0 : 1;
618 			case Type.object:
619 				enforce(false, "JSON objects cannot be compared.");
620 				assert(false);
621 		}
622 	}
623 
624 
625 
626 	/**
627 		Returns the type id corresponding to the given D type.
628 	*/
629 	static @property Type typeId(T)() {
630 		static if( is(T == typeof(null)) ) return Type.null_;
631 		else static if( is(T == bool) ) return Type.bool_;
632 		else static if( is(T == double) ) return Type.float_;
633 		else static if( is(T == float) ) return Type.float_;
634 		else static if( is(T : long) ) return Type.int_;
635 		else static if( is(T == string) ) return Type..string;
636 		else static if( is(T == Json[]) ) return Type.array;
637 		else static if( is(T == Json[string]) ) return Type.object;
638 		else static assert(false, "Unsupported JSON type '"~T.stringof~"'. Only bool, long, double, string, Json[] and Json[string] are allowed.");
639 	}
640 
641 	/**
642 		Returns the JSON object as a string.
643 
644 		For large JSON values use writeJsonString instead as this function will store the whole string
645 		in memory, whereas writeJsonString writes it out bit for bit.
646 
647 		See_Also: writeJsonString, toPrettyString
648 	*/
649 	string toString()
650 	const {
651 		auto ret = appender!string();
652 		writeJsonString(ret, this);
653 		return ret.data;
654 	}
655 
656 	/**
657 		Returns the JSON object as a "pretty" string.
658 
659 		---
660 		auto json = Json(["foo": Json("bar")]);
661 		writeln(json.toPrettyString());
662 
663 		// output:
664 		// {
665 		//     "foo": "bar"
666 		// }
667 		---
668 
669 		Params:
670 			level = Specifies the base amount of indentation for the output. Indentation is always
671 				done using tab characters.
672 
673 		See_Also: writePrettyJsonString, toString
674 	*/
675 	string toPrettyString(int level = 0)
676 	const {
677 		auto ret = appender!string();
678 		writePrettyJsonString(ret, this, level);
679 		return ret.data;
680 	}
681 
682 	private void checkType(T...)(string op = null)
683 	const {
684 		bool found = false;
685 		foreach (t; T) if (m_type == typeId!t) found = true;
686 		if (found) return;
687 		if (T.length == 1) {
688 			throw new Exception(format("Got %s - expected %s.", this.displayName, typeId!(T[0]).to!string));
689 		} else {
690 			string types;
691 			foreach (t; T) {
692 				if (types.length) types ~= ", ";
693 				types ~= typeId!t.to!string;
694 			}
695 			throw new Exception(format("Got %s - expected one of %s.", this.displayName, types));
696 		}
697 	}
698 
699 	private @property string displayName()
700 	const {
701 		if (m_name.length) return m_name ~ " of type " ~ m_type.to!string();
702 		else return "JSON of type " ~ m_type.to!string();
703 	}
704 
705 	/*invariant()
706 	{
707 		assert(m_type >= Type.undefined && m_type <= Type.object);
708 	}*/
709 }
710 
711 
712 /******************************************************************************/
713 /* public functions                                                           */
714 /******************************************************************************/
715 
716 /**
717 	Parses the given range as a JSON string and returns the corresponding Json object.
718 
719 	The range is shrunk during parsing, leaving any remaining text that is not part of
720 	the JSON contents.
721 
722 	Throws an Exception if any parsing error occured.
723 */
724 Json parseJson(R)(ref R range, string filename, int* line)
725 	if( is(R == string) )
726 {
727 	import std.algorithm : min;
728 
729 	assert(line !is null);
730 	Json ret;
731 	enforceJson(!range.empty, "JSON string is empty.", filename, 0);
732 
733 	skipWhitespace(range, line);
734 
735 	version(JsonLineNumbers){
736 		import dub.internal.vibecompat.core.log;
737 		int curline = line ? *line : 0;
738 	}
739 
740 	switch( range.front ){
741 		case 'f':
742 			enforceJson(range[1 .. $].startsWith("alse"), "Expected 'false', got '"~range[0 .. min($, 5)]~"'.", filename, *line);
743 			range.popFrontN(5);
744 			ret = false;
745 			break;
746 		case 'n':
747 			enforceJson(range[1 .. $].startsWith("ull"), "Expected 'null', got '"~range[0 .. min($, 4)]~"'.", filename, *line);
748 			range.popFrontN(4);
749 			ret = null;
750 			break;
751 		case 't':
752 			enforceJson(range[1 .. $].startsWith("rue"), "Expected 'true', got '"~range[0 .. min($, 4)]~"'.", filename, *line);
753 			range.popFrontN(4);
754 			ret = true;
755 			break;
756 		case '0': .. case '9'+1:
757 		case '-':
758 			bool is_float;
759 			auto num = skipNumber(range, is_float, filename, *line);
760 			if( is_float ) ret = to!double(num);
761 			else ret = to!long(num);
762 			break;
763 		case '\"':
764 			ret = skipJsonString(range, filename, line);
765 			break;
766 		case '[':
767 			Json[] arr;
768 			range.popFront();
769 			while(true) {
770 				skipWhitespace(range, line);
771 				enforceJson(!range.empty, "Missing ']' before EOF.", filename, *line);
772 				if(range.front == ']') break;
773 				arr ~= parseJson(range, filename, line);
774 				skipWhitespace(range, line);
775 				enforceJson(!range.empty && (range.front == ',' || range.front == ']'), "Expected ']' or ','.", filename, *line);
776 				if( range.front == ']' ) break;
777 				else range.popFront();
778 			}
779 			range.popFront();
780 			ret = arr;
781 			break;
782 		case '{':
783 			Json[string] obj;
784 			range.popFront();
785 			while(true) {
786 				skipWhitespace(range, line);
787 				enforceJson(!range.empty, "Missing '}' before EOF.", filename, *line);
788 				if(range.front == '}') break;
789 				string key = skipJsonString(range, filename, line);
790 				skipWhitespace(range, line);
791 				enforceJson(range.startsWith(":"), "Expected ':' for key '" ~ key ~ "'", filename, *line);
792 				range.popFront();
793 				skipWhitespace(range, line);
794 				Json itm = parseJson(range, filename, line);
795 				obj[key] = itm;
796 				skipWhitespace(range, line);
797 				enforceJson(!range.empty && (range.front == ',' || range.front == '}'), "Expected '}' or ',' - got '"~range[0]~"'.", filename, *line);
798 				if( range.front == '}' ) break;
799 				else range.popFront();
800 			}
801 			range.popFront();
802 			ret = obj;
803 			break;
804 		default:
805 			enforceJson(false, "Expected valid json token, got '"~range[0 .. min($, 12)]~"'.", filename, *line);
806 	}
807 
808 	assert(ret.type != Json.Type.undefined);
809 	version(JsonLineNumbers) ret.line = curline;
810 	return ret;
811 }
812 
813 /**
814 	Parses the given JSON string and returns the corresponding Json object.
815 
816 	Throws an Exception if any parsing error occurs.
817 */
818 Json parseJsonString(string str, string filename = null)
819 {
820 	int line = 0;
821 	auto ret = parseJson(str, filename, &line);
822 	enforceJson(str.strip().length == 0, "Expected end of string after JSON value, not '"~str.strip()~"'.", filename, line);
823 	return ret;
824 }
825 
826 unittest {
827 	assert(parseJsonString("null") == Json(null));
828 	assert(parseJsonString("true") == Json(true));
829 	assert(parseJsonString("false") == Json(false));
830 	assert(parseJsonString("1") == Json(1));
831 	assert(parseJsonString("2.0") == Json(2.0));
832 	assert(parseJsonString("\"test\"") == Json("test"));
833 	assert(parseJsonString("[1, 2, 3]") == Json([Json(1), Json(2), Json(3)]));
834 	assert(parseJsonString("{\"a\": 1}") == Json(["a": Json(1)]));
835 	assert(parseJsonString(`"\\\/\b\f\n\r\t\u1234"`).get!string == "\\/\b\f\n\r\t\u1234");
836 }
837 
838 
839 /**
840 	Serializes the given value to JSON.
841 
842 	The following types of values are supported:
843 
844 	$(DL
845 		$(DT Json)            $(DD Used as-is)
846 		$(DT null)            $(DD Converted to Json.Type.null_)
847 		$(DT bool)            $(DD Converted to Json.Type.bool_)
848 		$(DT float, double)   $(DD Converted to Json.Type.Double)
849 		$(DT short, ushort, int, uint, long, ulong) $(DD Converted to Json.Type.int_)
850 		$(DT string)          $(DD Converted to Json.Type.string)
851 		$(DT T[])             $(DD Converted to Json.Type.array)
852 		$(DT T[string])       $(DD Converted to Json.Type.object)
853 		$(DT struct)          $(DD Converted to Json.Type.object)
854 		$(DT class)           $(DD Converted to Json.Type.object or Json.Type.null_)
855 	)
856 
857 	All entries of an array or an associative array, as well as all R/W properties and
858 	all public fields of a struct/class are recursively serialized using the same rules.
859 
860 	Fields ending with an underscore will have the last underscore stripped in the
861 	serialized output. This makes it possible to use fields with D keywords as their name
862 	by simply appending an underscore.
863 
864 	The following methods can be used to customize the serialization of structs/classes:
865 
866 	---
867 	Json toJson() const;
868 	static T fromJson(Json src);
869 
870 	string toString() const;
871 	static T fromString(string src);
872 	---
873 
874 	The methods will have to be defined in pairs. The first pair that is implemented by
875 	the type will be used for serialization (i.e. toJson overrides toString).
876 */
877 Json serializeToJson(T)(T value)
878 {
879 	alias TU = Unqual!T;
880 	static if( is(TU == Json) ) return value;
881 	else static if( is(TU == typeof(null)) ) return Json(null);
882 	else static if( is(TU == bool) ) return Json(value);
883 	else static if( is(TU == float) ) return Json(cast(double)value);
884 	else static if( is(TU == double) ) return Json(value);
885 	else static if( is(TU == DateTime) ) return Json(value.toISOExtString());
886 	else static if( is(TU == SysTime) ) return Json(value.toISOExtString());
887 	else static if( is(TU : long) ) return Json(cast(long)value);
888 	else static if( is(TU == string) ) return Json(value);
889 	else static if( isArray!T ){
890 		auto ret = new Json[value.length];
891 		foreach( i; 0 .. value.length )
892 			ret[i] = serializeToJson(value[i]);
893 		return Json(ret);
894 	} else static if( isAssociativeArray!TU ){
895 		Json[string] ret;
896 		foreach( string key, value; value )
897 			ret[key] = serializeToJson(value);
898 		return Json(ret);
899 	} else static if( isJsonSerializable!TU ){
900 		return value.toJson();
901 	} else static if( isStringSerializable!TU ){
902 		return Json(value.toString());
903 	} else static if( is(TU == struct) ){
904 		Json[string] ret;
905 		foreach( m; __traits(allMembers, T) ){
906 			static if( isRWField!(TU, m) ){
907 				auto mv = __traits(getMember, value, m);
908 				ret[underscoreStrip(m)] = serializeToJson(mv);
909 			}
910 		}
911 		return Json(ret);
912 	} else static if( is(TU == class) ){
913 		if( value is null ) return Json(null);
914 		Json[string] ret;
915 		foreach( m; __traits(allMembers, T) ){
916 			static if( isRWField!(TU, m) ){
917 				auto mv = __traits(getMember, value, m);
918 				ret[underscoreStrip(m)] = serializeToJson(mv);
919 			}
920 		}
921 		return Json(ret);
922 	} else static if( isPointer!TU ){
923 		if( value is null ) return Json(null);
924 		return serializeToJson(*value);
925 	} else {
926 		static assert(false, "Unsupported type '"~T.stringof~"' for JSON serialization.");
927 	}
928 }
929 
930 
931 /**
932 	Deserializes a JSON value into the destination variable.
933 
934 	The same types as for serializeToJson() are supported and handled inversely.
935 */
936 void deserializeJson(T)(ref T dst, Json src)
937 {
938 	dst = deserializeJson!T(src);
939 }
940 /// ditto
941 T deserializeJson(T)(Json src)
942 {
943 	static if( is(T == Json) ) return src;
944 	else static if( is(T == typeof(null)) ){ return null; }
945 	else static if( is(T == bool) ) return src.get!bool;
946 	else static if( is(T == float) ) return src.to!float;   // since doubles are frequently serialized without
947 	else static if( is(T == double) ) return src.to!double; // a decimal point, we allow conversions here
948 	else static if( is(T == DateTime) ) return DateTime.fromISOExtString(src.get!string);
949 	else static if( is(T == SysTime) ) return SysTime.fromISOExtString(src.get!string);
950 	else static if( is(T : long) ) return cast(T)src.get!long;
951 	else static if( is(T == string) ) return src.get!string;
952 	else static if( isArray!T ){
953 		alias TV = typeof(T.init[0]) ;
954 		auto dst = new Unqual!TV[src.length];
955 		foreach( size_t i, v; src )
956 			dst[i] = deserializeJson!(Unqual!TV)(v);
957 		return dst;
958 	} else static if( isAssociativeArray!T ){
959 		alias TV = typeof(T.init.values[0]) ;
960 		Unqual!TV[string] dst;
961 		foreach( string key, value; src )
962 			dst[key] = deserializeJson!(Unqual!TV)(value);
963 		return dst;
964 	} else static if( isJsonSerializable!T ){
965 		return T.fromJson(src);
966 	} else static if( isStringSerializable!T ){
967 		return T.fromString(src.get!string);
968 	} else static if( is(T == struct) ){
969 		T dst;
970 		foreach( m; __traits(allMembers, T) ){
971 			static if( isRWPlainField!(T, m) || isRWField!(T, m) ){
972 				alias TM = typeof(__traits(getMember, dst, m)) ;
973 				__traits(getMember, dst, m) = deserializeJson!TM(src[underscoreStrip(m)]);
974 			}
975 		}
976 		return dst;
977 	} else static if( is(T == class) ){
978 		if( src.type == Json.Type.null_ ) return null;
979 		auto dst = new T;
980 		foreach( m; __traits(allMembers, T) ){
981 			static if( isRWPlainField!(T, m) || isRWField!(T, m) ){
982 				alias TM = typeof(__traits(getMember, dst, m)) ;
983 				__traits(getMember, dst, m) = deserializeJson!TM(src[underscoreStrip(m)]);
984 			}
985 		}
986 		return dst;
987 	} else static if( isPointer!T ){
988 		if( src.type == Json.Type.null_ ) return null;
989 		alias TD = typeof(*T.init) ;
990 		dst = new TD;
991 		*dst = deserializeJson!TD(src);
992 		return dst;
993 	} else {
994 		static assert(false, "Unsupported type '"~T.stringof~"' for JSON serialization.");
995 	}
996 }
997 
998 unittest {
999 	import std.stdio;
1000 	static struct S { float a; double b; bool c; int d; string e; byte f; ubyte g; long h; ulong i; float[] j; }
1001 	immutable S t = {1.5, -3.0, true, int.min, "Test", -128, 255, long.min, ulong.max, [1.1, 1.2, 1.3]};
1002 	S u;
1003 	deserializeJson(u, serializeToJson(t));
1004 	assert(t.a == u.a);
1005 	assert(t.b == u.b);
1006 	assert(t.c == u.c);
1007 	assert(t.d == u.d);
1008 	assert(t.e == u.e);
1009 	assert(t.f == u.f);
1010 	assert(t.g == u.g);
1011 	assert(t.h == u.h);
1012 	assert(t.i == u.i);
1013 	assert(t.j == u.j);
1014 }
1015 
1016 unittest {
1017 	static class C {
1018 		int a;
1019 		private int _b;
1020 		@property int b() const { return _b; }
1021 		@property void b(int v) { _b = v; }
1022 
1023 		@property int test() const { return 10; }
1024 
1025 		void test2() {}
1026 	}
1027 	C c = new C;
1028 	c.a = 1;
1029 	c.b = 2;
1030 
1031 	C d;
1032 	deserializeJson(d, serializeToJson(c));
1033 	assert(c.a == d.a);
1034 	assert(c.b == d.b);
1035 }
1036 
1037 
1038 /**
1039 	Writes the given JSON object as a JSON string into the destination range.
1040 
1041 	This function will convert the given JSON value to a string without adding
1042 	any white space between tokens (no newlines, no indentation and no padding).
1043 	The output size is thus minizized, at the cost of bad human readability.
1044 
1045 	Params:
1046 		dst   = References the string output range to which the result is written.
1047 		json  = Specifies the JSON value that is to be stringified.
1048 
1049 	See_Also: Json.toString, writePrettyJsonString
1050 */
1051 void writeJsonString(R)(ref R dst, in Json json)
1052 //	if( isOutputRange!R && is(ElementEncodingType!R == char) )
1053 {
1054 	final switch( json.type ){
1055 		case Json.Type.undefined: dst.put("undefined"); break;
1056 		case Json.Type.null_: dst.put("null"); break;
1057 		case Json.Type.bool_: dst.put(cast(bool)json ? "true" : "false"); break;
1058 		case Json.Type.int_: formattedWrite(dst, "%d", json.get!long); break;
1059 		case Json.Type.float_: formattedWrite(dst, "%.16g", json.get!double); break;
1060 		case Json.Type..string:
1061 			dst.put("\"");
1062 			jsonEscape(dst, cast(string)json);
1063 			dst.put("\"");
1064 			break;
1065 		case Json.Type.array:
1066 			dst.put("[");
1067 			bool first = true;
1068 			foreach( ref const Json e; json ){
1069 				if( e.type == Json.Type.undefined ) continue;
1070 				if( !first ) dst.put(",");
1071 				first = false;
1072 				writeJsonString(dst, e);
1073 			}
1074 			dst.put("]");
1075 			break;
1076 		case Json.Type.object:
1077 			dst.put("{");
1078 			bool first = true;
1079 			foreach( string k, ref const Json e; json ){
1080 				if( e.type == Json.Type.undefined ) continue;
1081 				if( !first ) dst.put(",");
1082 				first = false;
1083 				dst.put("\"");
1084 				jsonEscape(dst, k);
1085 				dst.put("\":");
1086 				writeJsonString(dst, e);
1087 			}
1088 			dst.put("}");
1089 			break;
1090 	}
1091 }
1092 
1093 /**
1094 	Writes the given JSON object as a prettified JSON string into the destination range.
1095 
1096 	The output will contain newlines and indents to make the output human readable.
1097 
1098 	Params:
1099 		dst   = References the string output range to which the result is written.
1100 		json  = Specifies the JSON value that is to be stringified.
1101 		level = Specifies the base amount of indentation for the output. Indentation is always
1102 			done using tab characters.
1103 
1104 	See_Also: Json.toPrettyString, writeJsonString
1105 */
1106 void writePrettyJsonString(R)(ref R dst, in Json json, int level = 0)
1107 //	if( isOutputRange!R && is(ElementEncodingType!R == char) )
1108 {
1109 	final switch( json.type ){
1110 		case Json.Type.undefined: dst.put("undefined"); break;
1111 		case Json.Type.null_: dst.put("null"); break;
1112 		case Json.Type.bool_: dst.put(cast(bool)json ? "true" : "false"); break;
1113 		case Json.Type.int_: formattedWrite(dst, "%d", json.get!long); break;
1114 		case Json.Type.float_: formattedWrite(dst, "%.16g", json.get!double); break;
1115 		case Json.Type..string:
1116 			dst.put("\"");
1117 			jsonEscape(dst, cast(string)json);
1118 			dst.put("\"");
1119 			break;
1120 		case Json.Type.array:
1121 			dst.put("[");
1122 			bool first = true;
1123 			foreach( e; json ){
1124 				if( e.type == Json.Type.undefined ) continue;
1125 				if( !first ) dst.put(",");
1126 				first = false;
1127 				dst.put("\n");
1128 				foreach( tab; 0 .. level+1 ) dst.put('\t');
1129 				writePrettyJsonString(dst, e, level+1);
1130 			}
1131 			if( json.length > 0 ) {
1132 				dst.put('\n');
1133 				foreach( tab; 0 .. level ) dst.put('\t');
1134 			}
1135 			dst.put("]");
1136 			break;
1137 		case Json.Type.object:
1138 			dst.put("{");
1139 			bool first = true;
1140 			foreach( string k, e; json ){
1141 				if( e.type == Json.Type.undefined ) continue;
1142 				if( !first ) dst.put(",");
1143 				dst.put("\n");
1144 				first = false;
1145 				foreach( tab; 0 .. level+1 ) dst.put('\t');
1146 				dst.put("\"");
1147 				jsonEscape(dst, k);
1148 				dst.put("\": ");
1149 				writePrettyJsonString(dst, e, level+1);
1150 			}
1151 			if( json.length > 0 ) {
1152 				dst.put('\n');
1153 				foreach( tab; 0 .. level ) dst.put('\t');
1154 			}
1155 			dst.put("}");
1156 			break;
1157 	}
1158 }
1159 
1160 /// private
1161 private void jsonEscape(R)(ref R dst, string s)
1162 {
1163 	foreach( ch; s ){
1164 		switch(ch){
1165 			default: dst.put(ch); break;
1166 			case '\\': dst.put("\\\\"); break;
1167 			case '\r': dst.put("\\r"); break;
1168 			case '\n': dst.put("\\n"); break;
1169 			case '\t': dst.put("\\t"); break;
1170 			case '\"': dst.put("\\\""); break;
1171 		}
1172 	}
1173 }
1174 
1175 /// private
1176 private string jsonUnescape(R)(ref R range)
1177 {
1178 	auto ret = appender!string();
1179 	while(!range.empty){
1180 		auto ch = range.front;
1181 		switch( ch ){
1182 			case '"': return ret.data;
1183 			case '\\':
1184 				range.popFront();
1185 				enforce(!range.empty, "Unterminated string escape sequence.");
1186 				switch(range.front){
1187 					default: enforce("Invalid string escape sequence."); break;
1188 					case '"': ret.put('\"'); range.popFront(); break;
1189 					case '\\': ret.put('\\'); range.popFront(); break;
1190 					case '/': ret.put('/'); range.popFront(); break;
1191 					case 'b': ret.put('\b'); range.popFront(); break;
1192 					case 'f': ret.put('\f'); range.popFront(); break;
1193 					case 'n': ret.put('\n'); range.popFront(); break;
1194 					case 'r': ret.put('\r'); range.popFront(); break;
1195 					case 't': ret.put('\t'); range.popFront(); break;
1196 					case 'u':
1197 						range.popFront();
1198 						dchar uch = 0;
1199 						foreach( i; 0 .. 4 ){
1200 							uch *= 16;
1201 							enforce(!range.empty, "Unicode sequence must be '\\uXXXX'.");
1202 							auto dc = range.front;
1203 							range.popFront();
1204 							if( dc >= '0' && dc <= '9' ) uch += dc - '0';
1205 							else if( dc >= 'a' && dc <= 'f' ) uch += dc - 'a' + 10;
1206 							else if( dc >= 'A' && dc <= 'F' ) uch += dc - 'A' + 10;
1207 							else enforce(false, "Unicode sequence must be '\\uXXXX'.");
1208 						}
1209 						ret.put(uch);
1210 						break;
1211 				}
1212 				break;
1213 			default:
1214 				ret.put(ch);
1215 				range.popFront();
1216 				break;
1217 		}
1218 	}
1219 	return ret.data;
1220 }
1221 
1222 private string skipNumber(ref string s, out bool is_float, string filename, int line)
1223 {
1224 	size_t idx = 0;
1225 	is_float = false;
1226 	if( s[idx] == '-' ) idx++;
1227 	if( s[idx] == '0' ) idx++;
1228 	else {
1229 		enforceJson(isDigit(s[idx++]), "Digit expected at beginning of number.", filename, line);
1230 		while( idx < s.length && isDigit(s[idx]) ) idx++;
1231 	}
1232 
1233 	if( idx < s.length && s[idx] == '.' ){
1234 		idx++;
1235 		is_float = true;
1236 		while( idx < s.length && isDigit(s[idx]) ) idx++;
1237 	}
1238 
1239 	if( idx < s.length && (s[idx] == 'e' || s[idx] == 'E') ){
1240 		idx++;
1241 		is_float = true;
1242 		if( idx < s.length && (s[idx] == '+' || s[idx] == '-') ) idx++;
1243 		enforceJson(idx < s.length && isDigit(s[idx]), "Expected exponent." ~ s[0 .. idx], filename, line);
1244 		idx++;
1245 		while( idx < s.length && isDigit(s[idx]) ) idx++;
1246 	}
1247 
1248 	string ret = s[0 .. idx];
1249 	s = s[idx .. $];
1250 	return ret;
1251 }
1252 
1253 private string skipJsonString(ref string s, string filename, int* line = null)
1254 {
1255 	enforceJson(s.length >= 2, "Too small for a string: '" ~ s ~ "'", filename, *line);
1256 	enforceJson(s[0] == '\"', "Expected string, not '" ~ s ~ "'", filename, *line);
1257 	s = s[1 .. $];
1258 	string ret = jsonUnescape(s);
1259 	enforce(s.length > 0 && s[0] == '\"', "Unterminated string literal.", filename, *line);
1260 	s = s[1 .. $];
1261 	return ret;
1262 }
1263 
1264 private void skipWhitespace(ref string s, int* line = null)
1265 {
1266 	while( s.length > 0 ){
1267 		switch( s[0] ){
1268 			default: return;
1269 			case ' ', '\t': s = s[1 .. $]; break;
1270 			case '\n':
1271 				s = s[1 .. $];
1272 				if( s.length > 0 && s[0] == '\r' ) s = s[1 .. $];
1273 				if( line ) (*line)++;
1274 				break;
1275 			case '\r':
1276 				s = s[1 .. $];
1277 				if( s.length > 0 && s[0] == '\n' ) s = s[1 .. $];
1278 				if( line ) (*line)++;
1279 				break;
1280 		}
1281 	}
1282 }
1283 
1284 /// private
1285 private bool isDigit(T)(T ch){ return ch >= '0' && ch <= '9'; }
1286 
1287 private string underscoreStrip(string field_name)
1288 {
1289 	if( field_name.length < 1 || field_name[$-1] != '_' ) return field_name;
1290 	else return field_name[0 .. $-1];
1291 }
1292 
1293 private template isJsonSerializable(T) { enum isJsonSerializable = is(typeof(T.init.toJson()) == Json) && is(typeof(T.fromJson(Json())) == T); }
1294 package template isStringSerializable(T) { enum isStringSerializable = is(typeof(T.init.toString()) == string) && is(typeof(T.fromString("")) == T); }
1295 
1296 private void enforceJson(string filename = __FILE__, int line = __LINE__)(bool cond, lazy string message, string err_file, int err_line)
1297 {
1298 	if (!cond) {
1299 		auto err_msg = format("%s(%s): Error: %s", err_file, err_line, message);
1300 		throw new Exception(err_msg, filename, line);
1301 	}
1302 }