1 /**
2  * JSON encoding.
3  *
4  * License:
5  *   This Source Code Form is subject to the terms of
6  *   the Mozilla Public License, v. 2.0. If a copy of
7  *   the MPL was not distributed with this file, You
8  *   can obtain one at http://mozilla.org/MPL/2.0/.
9  *
10  * Authors:
11  *   Vladimir Panteleev <vladimir@thecybershadow.net>
12  */
13 
14 module ae.utils.json;
15 
16 import std.exception;
17 import std.string;
18 import std.traits;
19 import std.typecons;
20 
21 import ae.utils.exception;
22 import ae.utils.meta;
23 import ae.utils.textout;
24 
25 // ************************************************************************
26 
27 struct JsonWriter(Output)
28 {
29 	/// You can set this to something to e.g. write to another buffer.
30 	Output output;
31 
32 	private void putChars(S...)(S strings)
33 	{
34 		static if (is(typeof(output.put(strings))))
35 			output.put(strings);
36 		else
37 			foreach (str; strings)
38 				static if (is(typeof(output.put(str))))
39 					output.put(str);
40 				else
41 					foreach (dchar c; str)
42 					{
43 						alias C = char; // TODO: get char type of output
44 						C[4 / C.sizeof] buf = void;
45 						auto size = encode(buf, c);
46 						output.put(buf[0..size]);
47 					}
48 	}
49 
50 	/// Write a string literal.
51 	private void putString(C)(in C[] s)
52 	{
53 		// TODO: escape Unicode characters?
54 		// TODO: Handle U+2028 and U+2029 ( http://timelessrepo.com/json-isnt-a-javascript-subset )
55 
56 		output.put('"');
57 		auto start = s.ptr, p = start, end = start+s.length;
58 
59 		while (p < end)
60 		{
61 			auto c = *p++;
62 			if (Escapes.escaped[c])
63 			{
64 				putChars(start[0..p-start-1], Escapes.chars[c]);
65 				start = p;
66 			}
67 		}
68 
69 		putChars(start[0..p-start], '"');
70 	}
71 
72 	/// Write a value of a simple type.
73 	void putValue(T)(T v)
74 	{
75 		static if (is(typeof(v is null)))
76 			if (v is null)
77 				return output.put("null");
78 		static if (is(T == typeof(null)))
79 			return output.put("null");
80 		else
81 		static if (isSomeString!T)
82 			putString(v);
83 		else
84 		static if (is(Unqual!T == bool))
85 			return output.put(v ? "true" : "false");
86 		else
87 		static if (is(Unqual!T : long))
88 			return .put(output, v);
89 		else
90 		static if (is(Unqual!T : real))
91 			return output.putFP(v);
92 		else
93 			static assert(0, "Don't know how to write " ~ T.stringof);
94 	}
95 
96 	void beginArray()
97 	{
98 		output.put('[');
99 	}
100 
101 	void endArray()
102 	{
103 		output.put(']');
104 	}
105 
106 	void beginObject()
107 	{
108 		output.put('{');
109 	}
110 
111 	void endObject()
112 	{
113 		output.put('}');
114 	}
115 
116 	void putKey(in char[] key)
117 	{
118 		putString(key);
119 		output.put(':');
120 	}
121 
122 	void putComma()
123 	{
124 		output.put(',');
125 	}
126 }
127 
128 struct PrettyJsonWriter(Output, alias indent = '\t', alias newLine = '\n', alias pad = ' ')
129 {
130 	JsonWriter!Output jsonWriter;
131 	alias jsonWriter this;
132 
133 	bool indentPending;
134 	uint indentLevel;
135 
136 	void putIndent()
137 	{
138 		if (indentPending)
139 		{
140 			foreach (n; 0..indentLevel)
141 				output.put(indent);
142 			indentPending = false;
143 		}
144 	}
145 
146 	void putNewline()
147 	{
148 		if (!indentPending)
149 		{
150 			output.put(newLine);
151 			indentPending = true;
152 		}
153 	}
154 
155 	void putValue(T)(T v)
156 	{
157 		putIndent();
158 		jsonWriter.putValue(v);
159 	}
160 
161 	void beginArray()
162 	{
163 		putIndent();
164 		jsonWriter.beginArray();
165 		indentLevel++;
166 		putNewline();
167 	}
168 
169 	void endArray()
170 	{
171 		indentLevel--;
172 		putNewline();
173 		putIndent();
174 		jsonWriter.endArray();
175 	}
176 
177 	void beginObject()
178 	{
179 		putIndent();
180 		jsonWriter.beginObject();
181 		indentLevel++;
182 		putNewline();
183 	}
184 
185 	void endObject()
186 	{
187 		indentLevel--;
188 		putNewline();
189 		putIndent();
190 		jsonWriter.endObject();
191 	}
192 
193 	void putKey(in char[] key)
194 	{
195 		putIndent();
196 		putString(key);
197 		output.put(pad, ':', pad);
198 	}
199 
200 	void putComma()
201 	{
202 		jsonWriter.putComma();
203 		putNewline();
204 	}
205 }
206 
207 struct CustomJsonSerializer(Writer)
208 {
209 	Writer writer;
210 
211 	void put(T)(T v)
212 	{
213 		static if (is(T X == Nullable!X))
214 			if (v.isNull)
215 				writer.putValue(null);
216 			else
217 				put(v.get);
218 		else
219 		static if (is(T == enum))
220 			put(to!string(v));
221 		else
222 		static if (isSomeString!T || is(Unqual!T : real))
223 			writer.putValue(v);
224 		else
225 		static if (is(T == typeof(null)))
226 			writer.putValue(null);
227 		else
228 		static if (is(T U : U[]))
229 		{
230 			writer.beginArray();
231 			if (v.length)
232 			{
233 				put(v[0]);
234 				foreach (i; v[1..$])
235 				{
236 					writer.putComma();
237 					put(i);
238 				}
239 			}
240 			writer.endArray();
241 		}
242 		else
243 		static if (isTuple!T)
244 		{
245 			// TODO: serialize as object if tuple has names
246 			enum N = v.expand.length;
247 			static if (N == 0)
248 				return;
249 			else
250 			static if (N == 1)
251 				put(v.expand[0]);
252 			else
253 			{
254 				writer.beginArray();
255 				foreach (n; RangeTuple!N)
256 				{
257 					static if (n)
258 						writer.putComma();
259 					put(v.expand[n]);
260 				}
261 				writer.endArray();
262 			}
263 		}
264 		else
265 		static if (is(typeof(T.init.keys)) && is(typeof(T.init.values)) && is(typeof({string s = T.init.keys[0];})))
266 		{
267 			writer.beginObject();
268 			bool first = true;
269 			foreach (key, value; v)
270 			{
271 				if (!first)
272 					writer.putComma();
273 				else
274 					first = false;
275 				writer.putKey(key);
276 				put(value);
277 			}
278 			writer.endObject();
279 		}
280 		else
281 		static if (is(T==JSONFragment))
282 			writer.output.put(v.json);
283 		else
284 		static if (is(T==struct))
285 		{
286 			writer.beginObject();
287 			bool first = true;
288 			foreach (i, field; v.tupleof)
289 			{
290 				static if (!doSkipSerialize!(T, v.tupleof[i].stringof[2..$]))
291 				{
292 					static if (hasAttribute!(JSONOptional, v.tupleof[i]))
293 						if (v.tupleof[i] == T.init.tupleof[i])
294 							continue;
295 					if (!first)
296 						writer.putComma();
297 					else
298 						first = false;
299 					writer.putKey(getJsonName!(T, v.tupleof[i].stringof[2..$]));
300 					put(field);
301 				}
302 			}
303 			writer.endObject();
304 		}
305 		else
306 		static if (is(typeof(*v)))
307 		{
308 			if (v)
309 				put(*v);
310 			else
311 				writer.putValue(null);
312 		}
313 		else
314 			static assert(0, "Can't serialize " ~ T.stringof ~ " to JSON");
315 	}
316 }
317 
318 alias JsonSerializer = CustomJsonSerializer!(JsonWriter!StringBuilder);
319 
320 private struct Escapes
321 {
322 	static immutable  string[256] chars;
323 	static immutable bool[256] escaped;
324 
325 	shared static this()
326 	{
327 		import std.string : format;
328 
329 		escaped[] = true;
330 		foreach (c; 0..256)
331 			if (c=='\\')
332 				chars[c] = `\\`;
333 			else
334 			if (c=='\"')
335 				chars[c] = `\"`;
336 			else
337 			if (c=='\b')
338 				chars[c] = `\b`;
339 			else
340 			if (c=='\f')
341 				chars[c] = `\f`;
342 			else
343 			if (c=='\n')
344 				chars[c] = `\n`;
345 			else
346 			if (c=='\r')
347 				chars[c] = `\r`;
348 			else
349 			if (c=='\t')
350 				chars[c] = `\t`;
351 			else
352 			if (c<'\x20' || c == '\x7F' || c=='<' || c=='>' || c=='&')
353 				chars[c] = format(`\u%04x`, c);
354 			else
355 				chars[c] = [cast(char)c],
356 				escaped[c] = false;
357 	}
358 }
359 
360 // ************************************************************************
361 
362 string toJson(T)(T v)
363 {
364 	JsonSerializer serializer;
365 	serializer.put(v);
366 	return serializer.writer.output.get();
367 }
368 
369 unittest
370 {
371 	struct X { int a; string b; }
372 	X x = {17, "aoeu"};
373 	assert(toJson(x) == `{"a":17,"b":"aoeu"}`, toJson(x));
374 	int[] arr = [1,5,7];
375 	assert(toJson(arr) == `[1,5,7]`);
376 	assert(toJson(true) == `true`);
377 
378 	assert(toJson(tuple()) == ``);
379 	assert(toJson(tuple(42)) == `42`);
380 	assert(toJson(tuple(42, "banana")) == `[42,"banana"]`);
381 }
382 
383 // ************************************************************************
384 
385 string toPrettyJson(T)(T v)
386 {
387 	CustomJsonSerializer!(PrettyJsonWriter!StringBuilder) serializer;
388 	serializer.put(v);
389 	return serializer.writer.output.get();
390 }
391 
392 unittest
393 {
394 	struct X { int a; string b; int[] c, d; }
395 	X x = {17, "aoeu", [1, 2, 3]};
396 	assert(toPrettyJson(x) ==
397 `{
398 	"a" : 17,
399 	"b" : "aoeu",
400 	"c" : [
401 		1,
402 		2,
403 		3
404 	],
405 	"d" : [
406 	]
407 }`, toPrettyJson(x));
408 }
409 
410 // ************************************************************************
411 
412 import std.ascii;
413 import std.utf;
414 import std.conv;
415 
416 import ae.utils.text;
417 
418 private struct JsonParser(C)
419 {
420 	C[] s;
421 	size_t p;
422 
423 	Unqual!C next()
424 	{
425 		enforce(p < s.length, "Out of data while parsing JSON stream");
426 		return s[p++];
427 	}
428 
429 	string readN(uint n)
430 	{
431 		string r;
432 		for (int i=0; i<n; i++)
433 			r ~= next();
434 		return r;
435 	}
436 
437 	Unqual!C peek()
438 	{
439 		enforce(p < s.length, "Out of data while parsing JSON stream");
440 		return s[p];
441 	}
442 
443 	@property bool eof() { return p == s.length; }
444 
445 	void skipWhitespace()
446 	{
447 		while (isWhite(peek()))
448 			p++;
449 	}
450 
451 	void expect(C c)
452 	{
453 		auto n = next();
454 		enforce(n==c, text("Expected ", c, ", got ", n));
455 	}
456 
457 	T read(T)()
458 	{
459 		static if (is(T == typeof(null)))
460 			return readNull();
461 		else
462 		static if (is(T X == Nullable!X))
463 			return readNullable!X();
464 		else
465 		static if (is(T==enum))
466 			return readEnum!(T)();
467 		else
468 		static if (isSomeString!T)
469 			return readString().to!T;
470 		else
471 		static if (is(T==bool))
472 			return readBool();
473 		else
474 		static if (is(T : real))
475 			return readNumber!(T)();
476 		else
477 		static if (isDynamicArray!T)
478 			return readArray!(typeof(T.init[0]))();
479 		else
480 		static if (isStaticArray!T)
481 		{
482 			T result = readArray!(typeof(T.init[0]))()[];
483 			return result;
484 		}
485 		else
486 		static if (isTuple!T)
487 			return readTuple!T();
488 		else
489 		static if (is(typeof(T.init.keys)) && is(typeof(T.init.values)) && is(typeof(T.init.keys[0])==string))
490 			return readAA!(T)();
491 		else
492 		static if (is(T==JSONFragment))
493 		{
494 			auto start = p;
495 			skipValue();
496 			return JSONFragment(s[start..p]);
497 		}
498 		else
499 		static if (is(T==struct))
500 			return readObject!(T)();
501 		else
502 		static if (is(T U : U*))
503 			return readPointer!T();
504 		else
505 			static assert(0, "Can't decode " ~ T.stringof ~ " from JSON");
506 	}
507 
508 	auto readTuple(T)()
509 	{
510 		// TODO: serialize as object if tuple has names
511 		enum N = T.expand.length;
512 		static if (N == 0)
513 			return T();
514 		else
515 		static if (N == 1)
516 			return T(read!(typeof(T.expand[0])));
517 		else
518 		{
519 			T v;
520 			expect('[');
521 			foreach (n, ref f; v.expand)
522 			{
523 				static if (n)
524 					expect(',');
525 				f = read!(typeof(f));
526 			}
527 			expect(']');
528 			return v;
529 		}
530 	}
531 
532 	typeof(null) readNull()
533 	{
534 		expect('n');
535 		expect('u');
536 		expect('l');
537 		expect('l');
538 		return null;
539 	}
540 
541 	auto readNullable(T)()
542 	{
543 		if (peek() == 'n')
544 		{
545 			readNull();
546 			return Nullable!T();
547 		}
548 		else
549 			return Nullable!T(read!T);
550 	}
551 
552 	C[] readSimpleString() /// i.e. without escapes
553 	{
554 		skipWhitespace();
555 		expect('"');
556 		auto start = p;
557 		while (true)
558 		{
559 			auto c = next();
560 			if (c=='"')
561 				break;
562 			else
563 			if (c=='\\')
564 				throw new Exception("Unexpected escaped character");
565 		}
566 		return s[start..p-1];
567 	}
568 
569 	C[] readString()
570 	{
571 		skipWhitespace();
572 		auto c = peek();
573 		if (c == '"')
574 		{
575 			next(); // '"'
576 			C[] result;
577 			auto start = p;
578 			while (true)
579 			{
580 				c = next();
581 				if (c=='"')
582 					break;
583 				else
584 				if (c=='\\')
585 				{
586 					result ~= s[start..p-1];
587 					switch (next())
588 					{
589 						case '"':  result ~= '"'; break;
590 						case '/':  result ~= '/'; break;
591 						case '\\': result ~= '\\'; break;
592 						case 'b':  result ~= '\b'; break;
593 						case 'f':  result ~= '\f'; break;
594 						case 'n':  result ~= '\n'; break;
595 						case 'r':  result ~= '\r'; break;
596 						case 't':  result ~= '\t'; break;
597 						case 'u':
598 						{
599 							wstring buf;
600 							goto Unicode_start;
601 
602 							while (s[p..$].startsWith(`\u`))
603 							{
604 								p+=2;
605 							Unicode_start:
606 								buf ~= cast(wchar)fromHex!ushort(readN(4));
607 							}
608 							result ~= buf.to!(C[]);
609 							break;
610 						}
611 						default: enforce(false, "Unknown escape");
612 					}
613 					start = p;
614 				}
615 			}
616 			result ~= s[start..p-1];
617 			return result;
618 		}
619 		else
620 		if (isDigit(c) || c=='-') // For languages that don't distinguish numeric strings from numbers
621 		{
622 			static immutable bool[256] numeric =
623 			[
624 				'0':true,
625 				'1':true,
626 				'2':true,
627 				'3':true,
628 				'4':true,
629 				'5':true,
630 				'6':true,
631 				'7':true,
632 				'8':true,
633 				'9':true,
634 				'.':true,
635 				'-':true,
636 				'+':true,
637 				'e':true,
638 				'E':true,
639 			];
640 
641 			auto start = p;
642 			while (numeric[c = peek()])
643 				p++;
644 			return s[start..p].dup;
645 		}
646 		else
647 		{
648 			foreach (n; "null")
649 				expect(n);
650 			return null;
651 		}
652 	}
653 
654 	bool readBool()
655 	{
656 		skipWhitespace();
657 		if (peek()=='t')
658 		{
659 			enforce(readN(4) == "true", "Bad boolean");
660 			return true;
661 		}
662 		else
663 		if (peek()=='f')
664 		{
665 			enforce(readN(5) == "false", "Bad boolean");
666 			return false;
667 		}
668 		else
669 		{
670 			ubyte i = readNumber!ubyte();
671 			enforce(i < 2, "Bad digit for implicit number-to-bool conversion");
672 			return !!i;
673 		}
674 	}
675 
676 	T readNumber(T)()
677 	{
678 		skipWhitespace();
679 		const(C)[] n;
680 		auto start = p;
681 		Unqual!C c = peek();
682 		if (c == '"')
683 			n = readSimpleString();
684 		else
685 		{
686 			while (c=='+' || c=='-' || (c>='0' && c<='9') || c=='e' || c=='E' || c=='.')
687 			{
688 				p++;
689 				if (eof) break;
690 				c = peek();
691 			}
692 			n = s[start..p];
693 		}
694 		static if (is(T : real))
695 			return to!T(n);
696 		else
697 			static assert(0, "Don't know how to parse numerical type " ~ T.stringof);
698 	}
699 
700 	T[] readArray(T)()
701 	{
702 		skipWhitespace();
703 		expect('[');
704 		skipWhitespace();
705 		T[] result;
706 		if (peek()==']')
707 		{
708 			p++;
709 			return result;
710 		}
711 		while(true)
712 		{
713 			result ~= read!(T)();
714 			skipWhitespace();
715 			if (peek()==']')
716 			{
717 				p++;
718 				return result;
719 			}
720 			else
721 				expect(',');
722 		}
723 	}
724 
725 	T readObject(T)()
726 	{
727 		skipWhitespace();
728 		expect('{');
729 		skipWhitespace();
730 		T v;
731 		if (peek()=='}')
732 		{
733 			p++;
734 			return v;
735 		}
736 
737 		while (true)
738 		{
739 			auto jsonField = readSimpleString();
740 			mixin(exceptionContext(q{"Error with field " ~ to!string(jsonField)}));
741 			skipWhitespace();
742 			expect(':');
743 
744 			bool found;
745 			foreach (i, ref field; v.tupleof)
746 			{
747 				enum name = getJsonName!(T, v.tupleof[i].stringof[2..$]);
748 				if (name == jsonField)
749 				{
750 					field = read!(typeof(v.tupleof[i]))();
751 					found = true;
752 					break;
753 				}
754 			}
755 
756 			if (!found)
757 			{
758 				static if (hasAttribute!(JSONPartial, T))
759 					skipValue();
760 				else
761 					throw new Exception(cast(string)("Unknown field " ~ jsonField));
762 			}
763 
764 			skipWhitespace();
765 			if (peek()=='}')
766 			{
767 				p++;
768 				return v;
769 			}
770 			else
771 				expect(',');
772 		}
773 	}
774 
775 	T readAA(T)()
776 	{
777 		skipWhitespace();
778 		static if (is(typeof(T.init is null)))
779 			if (peek() == 'n')
780 				return readNull();
781 		expect('{');
782 		skipWhitespace();
783 		T v;
784 		if (peek()=='}')
785 		{
786 			p++;
787 			return v;
788 		}
789 		alias K = typeof(v.keys[0]);
790 
791 		while (true)
792 		{
793 			auto jsonField = readString();
794 			skipWhitespace();
795 			expect(':');
796 
797 			v[jsonField.to!K] = read!(typeof(v.values[0]))();
798 
799 			skipWhitespace();
800 			if (peek()=='}')
801 			{
802 				p++;
803 				return v;
804 			}
805 			else
806 				expect(',');
807 		}
808 	}
809 
810 	T readEnum(T)()
811 	{
812 		return to!T(readSimpleString());
813 	}
814 
815 	T readPointer(T)()
816 	{
817 		skipWhitespace();
818 		if (peek()=='n')
819 		{
820 			enforce(readN(4) == "null", "Null expected");
821 			return null;
822 		}
823 		alias S = typeof(*T.init);
824 		T v = new S;
825 		*v = read!S();
826 		return v;
827 	}
828 
829 	void skipValue()
830 	{
831 		skipWhitespace();
832 		C c = peek();
833 		switch (c)
834 		{
835 			case '"':
836 				readString(); // TODO: Optimize
837 				break;
838 			case '0': .. case '9':
839 			case '-':
840 				readNumber!real(); // TODO: Optimize
841 				break;
842 			case '{':
843 				next();
844 				skipWhitespace();
845 				bool first = true;
846 				while (peek() != '}')
847 				{
848 					if (first)
849 						first = false;
850 					else
851 						expect(',');
852 					skipValue(); // key
853 					skipWhitespace();
854 					expect(':');
855 					skipValue(); // value
856 					skipWhitespace();
857 				}
858 				expect('}');
859 				break;
860 			case '[':
861 				next();
862 				skipWhitespace();
863 				bool first = true;
864 				while (peek() != ']')
865 				{
866 					if (first)
867 						first = false;
868 					else
869 						expect(',');
870 					skipValue();
871 					skipWhitespace();
872 				}
873 				expect(']');
874 				break;
875 			case 't':
876 				foreach (l; "true")
877 					expect(l);
878 				break;
879 			case 'f':
880 				foreach (l; "false")
881 					expect(l);
882 				break;
883 			case 'n':
884 				foreach (l; "null")
885 					expect(l);
886 				break;
887 			default:
888 				throw new Exception(text("Can't parse: ", c));
889 		}
890 	}
891 }
892 
893 T jsonParse(T, C)(C[] s)
894 {
895 	auto parser = JsonParser!C(s);
896 	mixin(exceptionContext(q{format("Error at position %d", parser.p)}));
897 	return parser.read!T();
898 }
899 
900 unittest
901 {
902 	struct S { int i; S[] arr; S* p0, p1; }
903 	S s = S(42, [S(1), S(2)], null, new S(15));
904 	auto s2 = jsonParse!S(toJson(s));
905 	//assert(s == s2); // Issue 3789
906 	assert(s.i == s2.i && s.arr == s2.arr && s.p0 is s2.p0 && *s.p1 == *s2.p1);
907 	jsonParse!S(toJson(s).dup);
908 
909 	assert(jsonParse!(Tuple!())(``) == tuple());
910 	assert(jsonParse!(Tuple!int)(`42`) == tuple(42));
911 	assert(jsonParse!(Tuple!(int, string))(`[42, "banana"]`) == tuple(42, "banana"));
912 
913 	assert(jsonParse!(string[string])(`null`) is null);
914 }
915 
916 unittest
917 {
918 	struct T { string s; wstring w; dstring d; }
919 	T t;
920 	auto s = t.toJson;
921 	assert(s == `{"s":null,"w":null,"d":null}`, s);
922 
923 	t.s = "foo";
924 	t.w = "bar"w;
925 	t.d = "baz"d;
926 	s = t.toJson;
927 	assert(s == `{"s":"foo","w":"bar","d":"baz"}`, s);
928 
929 	jsonParse!T(s);
930 	jsonParse!T(cast(char[]) s);
931 	jsonParse!T(cast(const(char)[]) s);
932 	jsonParse!T(s.to!wstring);
933 	jsonParse!T(s.to!dstring);
934 }
935 
936 // ************************************************************************
937 
938 // TODO: migrate to UDAs
939 
940 /**
941  * A template that designates fields which should not be serialized to Json.
942  *
943  * Example:
944  * ---
945  * struct Point { int x, y, z; mixin NonSerialized!(x, z); }
946  * assert(jsonParse!Point(toJson(Point(1, 2, 3))) == Point(0, 2, 0));
947  * ---
948  */
949 template NonSerialized(fields...)
950 {
951 	import ae.utils.meta : stringofArray;
952 	mixin(mixNonSerializedFields(stringofArray!fields()));
953 }
954 
955 string mixNonSerializedFields(string[] fields)
956 {
957 	string result;
958 	foreach (field; fields)
959 		result ~= "enum bool " ~ field ~ "_nonSerialized = 1;";
960 	return result;
961 }
962 
963 private template doSkipSerialize(T, string member)
964 {
965 	enum bool doSkipSerialize = __traits(hasMember, T, member ~ "_nonSerialized");
966 }
967 
968 unittest
969 {
970 	struct Point { int x, y, z; mixin NonSerialized!(x, z); }
971 	assert(jsonParse!Point(toJson(Point(1, 2, 3))) == Point(0, 2, 0));
972 }
973 
974 unittest
975 {
976 	enum En { one, two }
977 	assert(En.one.toJson() == `"one"`);
978 	struct S { int i1, i2; S[] arr1, arr2; string[string] dic; En en; mixin NonSerialized!(i2, arr2); }
979 	S s = S(42, 5, [S(1), S(2)], [S(3), S(4)], ["apple":"fruit", "pizza":"vegetable"], En.two);
980 	auto s2 = jsonParse!S(toJson(s));
981 	assert(s.i1 == s2.i1 && s2.i2 is int.init && s.arr1 == s2.arr1 && s2.arr2 is null && s.dic == s2.dic && s.en == En.two);
982 }
983 
984 unittest
985 {
986 	alias B = Nullable!bool;
987 	B b;
988 
989 	b = jsonParse!B("true");
990 	assert(!b.isNull);
991 	assert(b.get == true);
992 	assert(b.toJson == "true");
993 
994 	b = jsonParse!B("false");
995 	assert(!b.isNull);
996 	assert(b.get == false);
997 	assert(b.toJson == "false");
998 
999 	b = jsonParse!B("null");
1000 	assert(b.isNull);
1001 	assert(b.toJson == "null");
1002 
1003 	struct S {}
1004 	alias NS = Nullable!S;
1005 	assert(NS.init.toJson == "null");
1006 }
1007 
1008 unittest // Issue 49
1009 {
1010 	immutable bool b;
1011 	assert(toJson(b) == "false");
1012 }
1013 
1014 unittest
1015 {
1016 	import ae.utils.aa : OrderedMap;
1017 	alias M = OrderedMap!(string, int);
1018 	M m;
1019 	m["one"] = 1;
1020 	m["two"] = 2;
1021 	auto j = (cast(const)m).toJson();
1022 	assert(j == `{"one":1,"two":2}`, j);
1023 	assert(j.jsonParse!M == m);
1024 }
1025 
1026 unittest
1027 {
1028 	assert(string.init.toJson.jsonParse!string  is null);
1029 	assert(""         .toJson.jsonParse!string !is null);
1030 }
1031 
1032 unittest
1033 {
1034 	char[] s = "{}".dup;
1035 	assert(s.jsonParse!(string[string]) == null);
1036 }
1037 
1038 unittest
1039 {
1040 	typeof(null) n;
1041 	assert(n.toJson.jsonParse!(typeof(null)) is null);
1042 }
1043 
1044 unittest
1045 {
1046 	double f = 1.5;
1047 	assert(f.toJson() == "1.5");
1048 }
1049 
1050 // ************************************************************************
1051 
1052 /// User-defined attribute - specify name for JSON object field.
1053 /// Useful when a JSON object may contain fields, the name of which are not valid D identifiers.
1054 struct JSONName { string name; }
1055 
1056 private template getJsonName(S, string FIELD)
1057 {
1058 	static if (hasAttribute!(JSONName, __traits(getMember, S, FIELD)))
1059 		enum getJsonName = getAttribute!(JSONName, __traits(getMember, S, FIELD)).name;
1060 	else
1061 		enum getJsonName = FIELD;
1062 }
1063 
1064 // ************************************************************************
1065 
1066 /// User-defined attribute - only serialize this field if its value is different from its .init value.
1067 struct JSONOptional {}
1068 
1069 unittest
1070 {
1071 	static struct S { @JSONOptional bool a=true, b=false; }
1072 	assert(S().toJson == `{}`, S().toJson);
1073 	assert(S(false, true).toJson == `{"a":false,"b":true}`);
1074 }
1075 
1076 // ************************************************************************
1077 
1078 /// User-defined attribute - skip unknown fields when deserializing.
1079 struct JSONPartial {}
1080 
1081 unittest
1082 {
1083 	@JSONPartial static struct S { int b; }
1084 	assert(`{"a":1,"b":2,"c":3.4,"d":[5,"x"],"de":[],"e":{"k":"v"},"ee":{},"f":true,"g":false,"h":null}`.jsonParse!S == S(2));
1085 }
1086 
1087 // ************************************************************************
1088 
1089 /// Fragment of raw JSON.
1090 /// When serialized, the .json field is inserted into the resulting
1091 /// string verbatim, without any validation.
1092 /// When deserialized, will contain the raw JSON of one JSON object of
1093 /// any type.
1094 struct JSONFragment
1095 {
1096 	string json;
1097 	bool opCast(T)() const if (is(T==bool)) { return !!json; }
1098 }
1099 
1100 unittest
1101 {
1102 	JSONFragment[] arr = [JSONFragment(`1`), JSONFragment(`true`), JSONFragment(`"foo"`), JSONFragment(`[55]`)];
1103 	assert(arr.toJson == `[1,true,"foo",[55]]`);
1104 	assert(arr.toJson.jsonParse!(JSONFragment[]) == arr);
1105 }