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