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