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 	/// Write a string literal.
33 	private void putString(in char[] s)
34 	{
35 		// TODO: escape Unicode characters?
36 		// TODO: Handle U+2028 and U+2029 ( http://timelessrepo.com/json-isnt-a-javascript-subset )
37 
38 		output.put('"');
39 		auto start = s.ptr, p = start, end = start+s.length;
40 
41 		while (p < end)
42 		{
43 			auto c = *p++;
44 			if (Escapes.escaped[c])
45 				output.put(start[0..p-start-1], Escapes.chars[c]),
46 				start = p;
47 		}
48 
49 		output.put(start[0..p-start], '"');
50 	}
51 
52 	/// Write a value of a simple type.
53 	void putValue(T)(T v)
54 	{
55 		static if (is(T == typeof(null)))
56 			return output.put("null");
57 		else
58 		static if (is(T : const(char)[]))
59 			putString(v);
60 		else
61 		static if (is(Unqual!T == bool))
62 			return output.put(v ? "true" : "false");
63 		else
64 		static if (is(Unqual!T : long))
65 			return .put(output, v);
66 		else
67 		static if (is(Unqual!T : real))
68 			return output.put(fpToString!T(v)); // TODO: don't allocate
69 		else
70 			static assert(0, "Don't know how to write " ~ T.stringof);
71 	}
72 
73 	void beginArray()
74 	{
75 		output.put('[');
76 	}
77 
78 	void endArray()
79 	{
80 		output.put(']');
81 	}
82 
83 	void beginObject()
84 	{
85 		output.put('{');
86 	}
87 
88 	void endObject()
89 	{
90 		output.put('}');
91 	}
92 
93 	void putKey(in char[] key)
94 	{
95 		putString(key);
96 		output.put(':');
97 	}
98 
99 	void putComma()
100 	{
101 		output.put(',');
102 	}
103 }
104 
105 struct PrettyJsonWriter(Output, alias indent = '\t', alias newLine = '\n', alias pad = ' ')
106 {
107 	JsonWriter!Output jsonWriter;
108 	alias jsonWriter this;
109 
110 	bool indentPending;
111 	uint indentLevel;
112 
113 	void putIndent()
114 	{
115 		if (indentPending)
116 		{
117 			foreach (n; 0..indentLevel)
118 				output.put(indent);
119 			indentPending = false;
120 		}
121 	}
122 
123 	void putNewline()
124 	{
125 		if (!indentPending)
126 		{
127 			output.put(newLine);
128 			indentPending = true;
129 		}
130 	}
131 
132 	void putValue(T)(T v)
133 	{
134 		putIndent();
135 		jsonWriter.putValue(v);
136 	}
137 
138 	void beginArray()
139 	{
140 		jsonWriter.beginArray();
141 		indentLevel++;
142 		putNewline();
143 	}
144 
145 	void endArray()
146 	{
147 		indentLevel--;
148 		putNewline();
149 		putIndent();
150 		jsonWriter.endArray();
151 	}
152 
153 	void beginObject()
154 	{
155 		putIndent();
156 		jsonWriter.beginObject();
157 		indentLevel++;
158 		putNewline();
159 	}
160 
161 	void endObject()
162 	{
163 		indentLevel--;
164 		putNewline();
165 		putIndent();
166 		jsonWriter.endObject();
167 	}
168 
169 	void putKey(in char[] key)
170 	{
171 		putIndent();
172 		putString(key);
173 		output.put(pad, ':', pad);
174 	}
175 
176 	void putComma()
177 	{
178 		jsonWriter.putComma();
179 		putNewline();
180 	}
181 }
182 
183 struct CustomJsonSerializer(Writer)
184 {
185 	Writer writer;
186 
187 	void put(T)(T v)
188 	{
189 		static if (is(T == enum))
190 			put(to!string(v));
191 		else
192 		static if (is(T : const(char)[]) || is(Unqual!T : real))
193 			writer.putValue(v);
194 		else
195 		static if (is(T U : U[]))
196 		{
197 			writer.beginArray();
198 			if (v.length)
199 			{
200 				put(v[0]);
201 				foreach (i; v[1..$])
202 				{
203 					writer.putComma();
204 					put(i);
205 				}
206 			}
207 			writer.endArray();
208 		}
209 		else
210 		static if (isTuple!T)
211 		{
212 			// TODO: serialize as object if tuple has names
213 			enum N = v.expand.length;
214 			static if (N == 0)
215 				return;
216 			else
217 			static if (N == 1)
218 				put(v.expand[0]);
219 			else
220 			{
221 				writer.beginArray();
222 				foreach (n; RangeTuple!N)
223 				{
224 					static if (n)
225 						writer.putComma();
226 					put(v.expand[n]);
227 				}
228 				writer.endArray();
229 			}
230 		}
231 		else
232 		static if (is(typeof(T.init.keys)) && is(typeof(T.init.values)) && is(typeof(T.init.keys[0])==string))
233 		{
234 			writer.beginObject();
235 			bool first = true;
236 			foreach (key, value; v)
237 			{
238 				if (!first)
239 					writer.putComma();
240 				else
241 					first = false;
242 				writer.putKey(key);
243 				put(value);
244 			}
245 			writer.endObject();
246 		}
247 		else
248 		static if (is(T==struct))
249 		{
250 			writer.beginObject();
251 			bool first = true;
252 			foreach (i, field; v.tupleof)
253 			{
254 				static if (!doSkipSerialize!(T, v.tupleof[i].stringof[2..$]))
255 				{
256 					static if (hasAttribute!(JSONOptional, v.tupleof[i]))
257 						if (v.tupleof[i] == T.init.tupleof[i])
258 							continue;
259 					if (!first)
260 						writer.putComma();
261 					else
262 						first = false;
263 					writer.putKey(getJsonName!(T, v.tupleof[i].stringof[2..$]));
264 					put(field);
265 				}
266 			}
267 			writer.endObject();
268 		}
269 		else
270 		static if (is(typeof(*v)))
271 		{
272 			if (v)
273 				put(*v);
274 			else
275 				writer.putValue(null);
276 		}
277 		else
278 			static assert(0, "Can't serialize " ~ T.stringof ~ " to JSON");
279 	}
280 }
281 
282 alias CustomJsonSerializer!(JsonWriter!StringBuilder) JsonSerializer;
283 
284 private struct Escapes
285 {
286 	static __gshared string[256] chars;
287 	static __gshared bool[256] escaped;
288 
289 	shared static this()
290 	{
291 		import std.string;
292 
293 		escaped[] = true;
294 		foreach (c; 0..256)
295 			if (c=='\\')
296 				chars[c] = `\\`;
297 			else
298 			if (c=='\"')
299 				chars[c] = `\"`;
300 			else
301 			if (c=='\b')
302 				chars[c] = `\b`;
303 			else
304 			if (c=='\f')
305 				chars[c] = `\f`;
306 			else
307 			if (c=='\n')
308 				chars[c] = `\n`;
309 			else
310 			if (c=='\r')
311 				chars[c] = `\r`;
312 			else
313 			if (c=='\t')
314 				chars[c] = `\t`;
315 			else
316 			if (c<'\x20' || c == '\x7F' || c=='<' || c=='>' || c=='&')
317 				chars[c] = format(`\u%04x`, c);
318 			else
319 				chars[c] = [cast(char)c],
320 				escaped[c] = false;
321 	}
322 }
323 
324 // ************************************************************************
325 
326 string toJson(T)(T v)
327 {
328 	JsonSerializer serializer;
329 	serializer.put(v);
330 	return serializer.writer.output.get();
331 }
332 
333 unittest
334 {
335 	struct X { int a; string b; }
336 	X x = {17, "aoeu"};
337 	assert(toJson(x) == `{"a":17,"b":"aoeu"}`, toJson(x));
338 	int[] arr = [1,5,7];
339 	assert(toJson(arr) == `[1,5,7]`);
340 	assert(toJson(true) == `true`);
341 
342 	assert(toJson(tuple()) == ``);
343 	assert(toJson(tuple(42)) == `42`);
344 	assert(toJson(tuple(42, "banana")) == `[42,"banana"]`);
345 }
346 
347 // ************************************************************************
348 
349 string toPrettyJson(T)(T v)
350 {
351 	CustomJsonSerializer!(PrettyJsonWriter!StringBuilder) serializer;
352 	serializer.put(v);
353 	return serializer.writer.output.get();
354 }
355 
356 unittest
357 {
358 	struct X { int a; string b; int[] c, d; }
359 	X x = {17, "aoeu", [1, 2, 3]};
360 	assert(toPrettyJson(x) ==
361 `{
362 	"a" : 17,
363 	"b" : "aoeu",
364 	"c" : [
365 		1,
366 		2,
367 		3
368 	],
369 	"d" : [
370 	]
371 }`, toPrettyJson(x));
372 }
373 
374 // ************************************************************************
375 
376 import std.ascii;
377 import std.utf;
378 import std.conv;
379 
380 import ae.utils.text;
381 
382 private struct JsonParser(C)
383 {
384 	C[] s;
385 	size_t p;
386 
387 	char next()
388 	{
389 		enforce(p < s.length);
390 		return s[p++];
391 	}
392 
393 	string readN(uint n)
394 	{
395 		string r;
396 		for (int i=0; i<n; i++)
397 			r ~= next();
398 		return r;
399 	}
400 
401 	char peek()
402 	{
403 		enforce(p < s.length);
404 		return s[p];
405 	}
406 
407 	@property bool eof() { return p == s.length; }
408 
409 	void skipWhitespace()
410 	{
411 		while (isWhite(peek()))
412 			p++;
413 	}
414 
415 	void expect(char c)
416 	{
417 		auto n = next();
418 		enforce(n==c, "Expected " ~ c ~ ", got " ~ n);
419 	}
420 
421 	T read(T)()
422 	{
423 		static if (is(T X == Nullable!X))
424 			return readNullable!X();
425 		else
426 		static if (is(T==enum))
427 			return readEnum!(T)();
428 		else
429 		static if (is(T==string))
430 			return readString();
431 		else
432 		static if (is(T==bool))
433 			return readBool();
434 		else
435 		static if (is(T : real))
436 			return readNumber!(T)();
437 		else
438 		static if (isDynamicArray!T)
439 			return readArray!(typeof(T.init[0]))();
440 		else
441 		static if (isStaticArray!T)
442 		{
443 			T result = readArray!(typeof(T.init[0]))()[];
444 			return result;
445 		}
446 		else
447 		static if (isTuple!T)
448 			return readTuple!T();
449 		else
450 		static if (is(typeof(T.init.keys)) && is(typeof(T.init.values)) && is(typeof(T.init.keys[0])==string))
451 			return readAA!(T)();
452 		else
453 		static if (is(T==struct))
454 			return readObject!(T)();
455 		else
456 		static if (is(T U : U*))
457 			return readPointer!T();
458 		else
459 			static assert(0, "Can't decode " ~ T.stringof ~ " from JSON");
460 	}
461 
462 	auto readTuple(T)()
463 	{
464 		// TODO: serialize as object if tuple has names
465 		enum N = T.expand.length;
466 		static if (N == 0)
467 			return T();
468 		else
469 		static if (N == 1)
470 			return T(read!(typeof(T.expand[0])));
471 		else
472 		{
473 			T v;
474 			expect('[');
475 			foreach (n, ref f; v.expand)
476 			{
477 				static if (n)
478 					expect(',');
479 				f = read!(typeof(f));
480 			}
481 			expect(']');
482 			return v;
483 		}
484 	}
485 
486 	auto readNullable(T)()
487 	{
488 		if (peek() == 'n')
489 		{
490 			next();
491 			expect('u');
492 			expect('l');
493 			expect('l');
494 			return Nullable!T();
495 		}
496 		else
497 			return Nullable!T(read!T);
498 	}
499 
500 	C[] readSimpleString() /// i.e. without escapes
501 	{
502 		skipWhitespace();
503 		expect('"');
504 		auto start = p;
505 		while (true)
506 		{
507 			auto c = next();
508 			if (c=='"')
509 				break;
510 			else
511 			if (c=='\\')
512 				throw new Exception("Unexpected escaped character");
513 		}
514 		return s[start..p-1];
515 	}
516 
517 	string readString()
518 	{
519 		skipWhitespace();
520 		auto c = peek();
521 		if (c == '"')
522 		{
523 			next(); // '"'
524 			string result;
525 			auto start = p;
526 			while (true)
527 			{
528 				c = next();
529 				if (c=='"')
530 					break;
531 				else
532 				if (c=='\\')
533 				{
534 					result ~= s[start..p-1];
535 					switch (next())
536 					{
537 						case '"':  result ~= '"'; break;
538 						case '/':  result ~= '/'; break;
539 						case '\\': result ~= '\\'; break;
540 						case 'b':  result ~= '\b'; break;
541 						case 'f':  result ~= '\f'; break;
542 						case 'n':  result ~= '\n'; break;
543 						case 'r':  result ~= '\r'; break;
544 						case 't':  result ~= '\t'; break;
545 						case 'u':
546 						{
547 							wstring buf;
548 							goto Unicode_start;
549 
550 							while (s[p..$].startsWith(`\u`))
551 							{
552 								p+=2;
553 							Unicode_start:
554 								buf ~= cast(wchar)fromHex!ushort(readN(4));
555 							}
556 							result ~= toUTF8(buf);
557 							break;
558 						}
559 						default: enforce(false, "Unknown escape");
560 					}
561 					start = p;
562 				}
563 			}
564 			result ~= s[start..p-1];
565 			return result;
566 		}
567 		else
568 		if (isDigit(c) || c=='-') // For languages that don't distinguish numeric strings from numbers
569 		{
570 			static immutable bool[256] numeric =
571 			[
572 				'0':true,
573 				'1':true,
574 				'2':true,
575 				'3':true,
576 				'4':true,
577 				'5':true,
578 				'6':true,
579 				'7':true,
580 				'8':true,
581 				'9':true,
582 				'.':true,
583 				'-':true,
584 				'+':true,
585 				'e':true,
586 				'E':true,
587 			];
588 
589 			auto start = p;
590 			while (numeric[c = peek()])
591 				p++;
592 			return s[start..p].idup;
593 		}
594 		else
595 		{
596 			foreach (n; "null")
597 				expect(n);
598 			return null;
599 		}
600 	}
601 
602 	bool readBool()
603 	{
604 		skipWhitespace();
605 		if (peek()=='t')
606 		{
607 			enforce(readN(4) == "true", "Bad boolean");
608 			return true;
609 		}
610 		else
611 		if (peek()=='f')
612 		{
613 			enforce(readN(5) == "false", "Bad boolean");
614 			return false;
615 		}
616 		else
617 		{
618 			ubyte i = readNumber!ubyte();
619 			enforce(i < 2);
620 			return !!i;
621 		}
622 	}
623 
624 	T readNumber(T)()
625 	{
626 		skipWhitespace();
627 		T v;
628 		const(char)[] n;
629 		auto start = p;
630 		char c = peek();
631 		if (c == '"')
632 			n = readSimpleString();
633 		else
634 		{
635 			while (c=='+' || c=='-' || (c>='0' && c<='9') || c=='e' || c=='E' || c=='.')
636 			{
637 				p++;
638 				if (eof) break;
639 				c=peek();
640 			}
641 			n = s[start..p];
642 		}
643 		static if (is(T : real))
644 			return to!T(n);
645 		else
646 			static assert(0, "Don't know how to parse numerical type " ~ T.stringof);
647 	}
648 
649 	T[] readArray(T)()
650 	{
651 		skipWhitespace();
652 		expect('[');
653 		skipWhitespace();
654 		T[] result;
655 		if (peek()==']')
656 		{
657 			p++;
658 			return result;
659 		}
660 		while(true)
661 		{
662 			result ~= read!(T)();
663 			skipWhitespace();
664 			if (peek()==']')
665 			{
666 				p++;
667 				return result;
668 			}
669 			else
670 				expect(',');
671 		}
672 	}
673 
674 	T readObject(T)()
675 	{
676 		skipWhitespace();
677 		expect('{');
678 		skipWhitespace();
679 		T v;
680 		if (peek()=='}')
681 		{
682 			p++;
683 			return v;
684 		}
685 
686 		while (true)
687 		{
688 			auto jsonField = readSimpleString();
689 			mixin(exceptionContext(q{"Error with field " ~ to!string(jsonField)}));
690 			skipWhitespace();
691 			expect(':');
692 
693 			bool found;
694 			foreach (i, ref field; v.tupleof)
695 			{
696 				enum name = getJsonName!(T, v.tupleof[i].stringof[2..$]);
697 				if (name == jsonField)
698 				{
699 					field = read!(typeof(v.tupleof[i]))();
700 					found = true;
701 					break;
702 				}
703 			}
704 			enforce(found, "Unknown field " ~ jsonField);
705 
706 			skipWhitespace();
707 			if (peek()=='}')
708 			{
709 				p++;
710 				return v;
711 			}
712 			else
713 				expect(',');
714 		}
715 	}
716 
717 	T readAA(T)()
718 	{
719 		skipWhitespace();
720 		expect('{');
721 		skipWhitespace();
722 		T v;
723 		if (peek()=='}')
724 		{
725 			p++;
726 			return v;
727 		}
728 
729 		while (true)
730 		{
731 			string jsonField = readString();
732 			skipWhitespace();
733 			expect(':');
734 
735 			v[jsonField] = read!(typeof(v.values[0]))();
736 
737 			skipWhitespace();
738 			if (peek()=='}')
739 			{
740 				p++;
741 				return v;
742 			}
743 			else
744 				expect(',');
745 		}
746 	}
747 
748 	T readEnum(T)()
749 	{
750 		return to!T(readSimpleString());
751 	}
752 
753 	T readPointer(T)()
754 	{
755 		skipWhitespace();
756 		if (peek()=='n')
757 		{
758 			enforce(readN(4) == "null", "Null expected");
759 			return null;
760 		}
761 		alias typeof(*T.init) S;
762 		T v = new S;
763 		*v = read!S();
764 		return v;
765 	}
766 }
767 
768 T jsonParse(T, C)(C[] s)
769 {
770 	auto parser = JsonParser!C(s);
771 	mixin(exceptionContext(q{format("Error at position %d", parser.p)}));
772 	return parser.read!T();
773 }
774 
775 unittest
776 {
777 	struct S { int i; S[] arr; S* p0, p1; }
778 	S s = S(42, [S(1), S(2)], null, new S(15));
779 	auto s2 = jsonParse!S(toJson(s));
780 	//assert(s == s2); // Issue 3789
781 	assert(s.i == s2.i && s.arr == s2.arr && s.p0 is s2.p0 && *s.p1 == *s2.p1);
782 	jsonParse!S(toJson(s).dup);
783 
784 	assert(jsonParse!(Tuple!())(``) == tuple());
785 	assert(jsonParse!(Tuple!int)(`42`) == tuple(42));
786 	assert(jsonParse!(Tuple!(int, string))(`[42, "banana"]`) == tuple(42, "banana"));
787 }
788 
789 // ************************************************************************
790 
791 // TODO: migrate to UDAs
792 
793 /**
794  * A template that designates fields which should not be serialized to Json.
795  *
796  * Example:
797  * ---
798  * struct Point { int x, y, z; mixin NonSerialized!(x, z); }
799  * assert(jsonParse!Point(toJson(Point(1, 2, 3))) == Point(0, 2, 0));
800  * ---
801  */
802 template NonSerialized(fields...)
803 {
804 	import ae.utils.meta : stringofArray;
805 	mixin(NonSerializedFields(stringofArray!fields()));
806 }
807 
808 string NonSerializedFields(string[] fields)
809 {
810 	string result;
811 	foreach (field; fields)
812 		result ~= "enum bool " ~ field ~ "_nonSerialized = 1;";
813 	return result;
814 }
815 
816 private template doSkipSerialize(T, string member)
817 {
818 	enum bool doSkipSerialize = __traits(hasMember, T, member ~ "_nonSerialized");
819 }
820 
821 unittest
822 {
823 	struct Point { int x, y, z; mixin NonSerialized!(x, z); }
824 	assert(jsonParse!Point(toJson(Point(1, 2, 3))) == Point(0, 2, 0));
825 }
826 
827 unittest
828 {
829 	enum En { one, two }
830 	assert(En.one.toJson() == `"one"`);
831 	struct S { int i1, i2; S[] arr1, arr2; string[string] dic; En en; mixin NonSerialized!(i2, arr2); }
832 	S s = S(42, 5, [S(1), S(2)], [S(3), S(4)], ["apple":"fruit", "pizza":"vegetable"], En.two);
833 	auto s2 = jsonParse!S(toJson(s));
834 	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);
835 }
836 
837 unittest
838 {
839 	alias B = Nullable!bool;
840 	B b;
841 
842 	b = jsonParse!B("true");
843 	assert(!b.isNull);
844 	assert(b == true);
845 
846 	b = jsonParse!B("false");
847 	assert(!b.isNull);
848 	assert(b == false);
849 
850 	b = jsonParse!B("null");
851 	assert(b.isNull);
852 }
853 
854 unittest // Issue 49
855 {
856 	immutable bool b;
857 	assert(toJson(b) == "false");
858 }
859 
860 unittest
861 {
862 	import ae.utils.aa;
863 	alias M = OrderedMap!(string, int);
864 	M m;
865 	m["one"] = 1;
866 	m["two"] = 2;
867 	auto j = m.toJson();
868 	assert(j == `{"one":1,"two":2}`, j);
869 	assert(j.jsonParse!M == m);
870 }
871 
872 // ************************************************************************
873 
874 /// User-defined attribute - specify name for JSON object field.
875 /// Useful when a JSON object may contain fields, the name of which are not valid D identifiers.
876 struct JSONName { string name; }
877 
878 private template getJsonName(S, string FIELD)
879 {
880 	static if (hasAttribute!(JSONName, __traits(getMember, S, FIELD)))
881 		enum getJsonName = getAttribute!(JSONName, __traits(getMember, S, FIELD)).name;
882 	else
883 		enum getJsonName = FIELD;
884 }
885 
886 // ************************************************************************
887 
888 /// User-defined attribute - only serialize this field if its value is different from its .init value.
889 struct JSONOptional {}
890 
891 unittest
892 {
893 	static struct S { @JSONOptional bool a=true, b=false; }
894 	assert(S().toJson == `{}`, S().toJson);
895 	assert(S(false, true).toJson == `{"a":false,"b":true}`);
896 }