1 /**
2 	Generic serialization framework.
3 
4 	This module provides general means for implementing (de-)serialization with
5 	a standardized behavior.
6 
7 	Supported_types:
8 		The following rules are applied in order when serializing or
9 		deserializing a certain type:
10 
11 		$(OL
12 			$(LI An `enum` type is serialized as its raw value, except if
13 				`@byName` is used, in which case the name of the enum value
14 				is serialized.)
15 			$(LI Any type that is specifically supported by the serializer
16 				is directly serialized. For example, the BSON serializer
17 				supports `BsonObjectID` directly.)
18 			$(LI Arrays and tuples (`std.typecons.Tuple`) are serialized
19 				using the array serialization functions where each element is
20 				serialized again according to these rules.)
21 			$(LI Associative arrays are serialized similar to arrays. The key
22 				type of the AA must satisfy the `isStringSerializable` trait
23 				and will always be serialized as a string.)
24 			$(LI Any `Nullable!T` will be serialized as either `null`, or
25 				as the contained value (subject to these rules again).)
26 			$(LI Any `Typedef!T` will be serialized as if it were just `T`.)
27 			$(LI Any `BitFlags!T` value will be serialized as `T[]`)
28 			$(LI Types satisfying the `isPolicySerializable` trait for the
29 				supplied `Policy` will be serialized as the value returned
30 				by the policy `toRepresentation` function (again subject to
31 				these rules).)
32 			$(LI Types satisfying the `isCustomSerializable` trait will be
33 				serialized as the value returned by their `toRepresentation`
34 				method (again subject to these rules).)
35 			$(LI Types satisfying the `isISOExtStringSerializable` trait will be
36 				serialized as a string, as returned by their `toISOExtString`
37 				method. This causes types such as `SysTime` to be serialized
38 				as strings.)
39 			$(LI Types satisfying the `isStringSinkSerializable` trait will be
40 				serialized as a string using the `toString(sink)` method. `sink`
41 				can either be a delegate that takes a `char` array argument, or
42 				an output range of `char`.)
43 			$(LI Types satisfying the `isStringSerializable` trait will be
44 				serialized as a string, as returned by their `toString`
45 				method.)
46 			$(LI Struct and class types by default will be serialized as
47 				associative arrays, where the key is the name of the
48 				corresponding field (can be overridden using the `@name`
49 				attribute). If the struct/class is annotated with `@asArray`,
50 				it will instead be serialized as a flat array of values in the
51 				order of declaration. Null class references will be serialized
52 				as `null`.)
53 			$(LI Pointer types will be serialized as either `null`, or as
54 				the value they point to.)
55 			$(LI Built-in integers and floating point values, as well as
56 				boolean values will be converted to strings, if the serializer
57 				doesn't support them directly.)
58 		)
59 
60 		Note that no aliasing detection is performed, so that pointers, class
61 		references and arrays referencing the same memory will be serialized
62 		as multiple copies. When in turn deserializing the data, they will also
63 		end up as separate copies in memory.
64 
65 	Field_names:
66 		By default, the field name of the serialized D type (for `struct` and
67 		`class` aggregates) is represented as-is in the serialized result. To
68 		circumvent name clashes with D's keywords, a single trailing underscore of
69 		any field name is stipped, so that a field name of `version_` results in
70 		just `"version"` as the serialized value. Names can also be freely
71 		customized using the `@name` annotation.
72 
73 		Associative array keys are always represented using their direct string
74 		representation.
75 
76 	Serializer_implementation:
77 		Serializers are implemented in terms of a struct with template methods that
78 		get called by the serialization framework:
79 
80 		---
81 		struct ExampleSerializer {
82 			enum isSupportedValueType(T) = is(T == string) || is(T == typeof(null));
83 
84 			// serialization
85 			auto getSerializedResult();
86 			void beginWriteDocument(TypeTraits)();
87 			void endWriteDocument(TypeTraits)();
88 			void beginWriteDictionary(TypeTraits)(size_t length); [OR] void beginWriteDictionary(TypeTraits)();
89 			void endWriteDictionary(TypeTraits)();
90 			void beginWriteDictionaryEntry(ElementTypeTraits)(string name);
91 			void endWriteDictionaryEntry(ElementTypeTraits)(string name);
92 			void beginWriteArray(TypeTraits)(size_t length);
93 			void endWriteArray(TypeTraits)();
94 			void beginWriteArrayEntry(ElementTypeTraits)(size_t index);
95 			void endWriteArrayEntry(ElementTypeTraits)(size_t index);
96 			void writeValue(TypeTraits, T)(T value);
97 
98 			// deserialization
99 
100 			void readDictionary(TypeTraits)(scope void delegate(string) entry_callback);
101 			void beginReadDictionaryEntry(ElementTypeTraits)(string);
102 			void endReadDictionaryEntry(ElementTypeTraits)(string);
103 			void readArray(TypeTraits)(scope void delegate(size_t) size_callback, scope void delegate() entry_callback);
104 			void beginReadArrayEntry(ElementTypeTraits)(size_t index);
105 			void endReadArrayEntry(ElementTypeTraits)(size_t index);
106 			T readValue(TypeTraits, T)();
107 			bool tryReadNull(TypeTraits)();
108 
109 			// skipValue() is optional. It will be called by the entry_callback in readDictionary
110 			// whenever the key passed to the entry_callback cannot be found.
111 			void skipValue();
112 		}
113 		---
114 
115 		The `TypeTraits` type passed to the individual methods has the following members:
116 		$(UL
117 			$(LI `Type`: The original type of the field to serialize)
118 			$(LI `Attributes`: User defined attributes attached to the field)
119 			$(LI `Policy`: An alias to the policy used for the serialization process)
120 		)
121 
122 		`ElementTypeTraits` have the following additional members:
123 		$(UL
124 			$(LI `ContainerType`: The original type of the enclosing container type)
125 			$(LI `ContainerAttributes`: User defined attributes attached to the enclosing container)
126 		)
127 
128 	Copyright: © 2013-2016 rejectedsoftware e.K.
129 	License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file.
130 	Authors: Sönke Ludwig
131 */
132 module vibe.data.serialization;
133 
134 import vibe.internal.meta.traits;
135 import vibe.internal.meta.uda;
136 
137 import std.array : Appender, appender;
138 import std.conv : to;
139 import std.exception : enforce;
140 import std.range.primitives : ElementType, isInputRange;
141 import std.traits;
142 import std.typetuple;
143 
144 
145 /**
146 	Serializes a value with the given serializer.
147 
148 	The serializer must have a value result for the first form
149 	to work. Otherwise, use the range based form.
150 
151 	See_Also: `vibe.data.json.JsonSerializer`, `vibe.data.json.JsonStringSerializer`, `vibe.data.bson.BsonSerializer`
152 */
153 auto serialize(Serializer, T, ARGS...)(auto ref T value, ARGS args)
154 {
155 	auto serializer = Serializer(args);
156 	serialize(serializer, value);
157 	return serializer.getSerializedResult();
158 }
159 /// ditto
160 void serialize(Serializer, T)(ref Serializer serializer, auto ref T value)
161 {
162 	serializeWithPolicy!(Serializer, DefaultPolicy)(serializer, value);
163 }
164 
165 /** Note that there is a convenience function `vibe.data.json.serializeToJson`
166 	that can be used instead of manually invoking `serialize`.
167 */
168 unittest {
169 	import vibe.data.json;
170 
171 	struct Test {
172 		int value;
173 		string text;
174 	}
175 
176 	Test test;
177 	test.value = 12;
178 	test.text = "Hello";
179 
180 	Json serialized = serialize!JsonSerializer(test);
181 	assert(serialized["value"].get!int == 12);
182 	assert(serialized["text"].get!string == "Hello");
183 }
184 
185 unittest {
186 	import vibe.data.json;
187 
188 	// Make sure that immutable(char[]) works just like string
189 	// (i.e., immutable(char)[]).
190 	immutable key = "answer";
191 	auto ints = [key: 42];
192 	auto serialized = serialize!JsonSerializer(ints);
193 	assert(serialized[key].get!int == 42);
194 }
195 
196 /**
197 	Serializes a value with the given serializer, representing values according to `Policy` when possible.
198 
199 	The serializer must have a value result for the first form
200 	to work. Otherwise, use the range based form.
201 
202 	See_Also: `vibe.data.json.JsonSerializer`, `vibe.data.json.JsonStringSerializer`, `vibe.data.bson.BsonSerializer`
203 */
204 auto serializeWithPolicy(Serializer, alias Policy, T, ARGS...)(auto ref T value, ARGS args)
205 {
206 	auto serializer = Serializer(args);
207 	serializeWithPolicy!(Serializer, Policy)(serializer, value);
208 	return serializer.getSerializedResult();
209 }
210 /// ditto
211 void serializeWithPolicy(Serializer, alias Policy, T)(ref Serializer serializer, auto ref T value)
212 {
213 	static if (is(typeof(serializer.beginWriteDocument!T())))
214 		serializer.beginWriteDocument!T();
215 	serializeValueImpl!(Serializer, Policy).serializeValue!T(serializer, value);
216 	static if (is(typeof(serializer.endWriteDocument!T())))
217 		serializer.endWriteDocument!T();
218 }
219 ///
220 version (unittest)
221 {
222 }
223 
224 ///
225 unittest {
226 	import vibe.data.json;
227 
228 	template SizePol(T)
229 		if (__traits(allMembers, T) == TypeTuple!("x", "y"))
230 	{
231 		import std.conv;
232 		import std.array;
233 
234 		static string toRepresentation(T value) @safe {
235 			return to!string(value.x) ~ "x" ~ to!string(value.y);
236 		}
237 
238 		static T fromRepresentation(string value) {
239 			string[] fields = value.split('x');
240 			alias fieldT = typeof(T.x);
241 			auto x = to!fieldT(fields[0]);
242 			auto y = to!fieldT(fields[1]);
243 			return T(x, y);
244 		}
245 	}
246 
247 	static struct SizeI {
248 		int x;
249 		int y;
250 	}
251 	SizeI sizeI = SizeI(1,2);
252 	Json serializedI = serializeWithPolicy!(JsonSerializer, SizePol)(sizeI);
253 	assert(serializedI.get!string == "1x2");
254 
255 	static struct SizeF {
256 		float x;
257 		float y;
258 	}
259 	SizeF sizeF = SizeF(0.1f,0.2f);
260 	Json serializedF = serializeWithPolicy!(JsonSerializer, SizePol)(sizeF);
261 	assert(serializedF.get!string == "0.1x0.2");
262 }
263 
264 
265 /**
266 	Deserializes and returns a serialized value.
267 
268 	serialized_data can be either an input range or a value containing
269 	the serialized data, depending on the type of serializer used.
270 
271 	See_Also: `vibe.data.json.JsonSerializer`, `vibe.data.json.JsonStringSerializer`, `vibe.data.bson.BsonSerializer`
272 */
273 T deserialize(Serializer, T, ARGS...)(ARGS args)
274 {
275 	return deserializeWithPolicy!(Serializer, DefaultPolicy, T)(args);
276 }
277 
278 /** Note that there is a convenience function `vibe.data.json.deserializeJson`
279 	that can be used instead of manually invoking `deserialize`.
280 */
281 unittest {
282 	import vibe.data.json;
283 
284 	struct Test {
285 		int value;
286 		string text;
287 	}
288 
289 	Json serialized = Json.emptyObject;
290 	serialized["value"] = 12;
291 	serialized["text"] = "Hello";
292 
293 	Test test = deserialize!(JsonSerializer, Test)(serialized);
294 	assert(test.value == 12);
295 	assert(test.text == "Hello");
296 }
297 
298 /**
299 	Deserializes and returns a serialized value, interpreting values according to `Policy` when possible.
300 
301 	serialized_data can be either an input range or a value containing
302 	the serialized data, depending on the type of serializer used.
303 
304 	See_Also: `vibe.data.json.JsonSerializer`, `vibe.data.json.JsonStringSerializer`, `vibe.data.bson.BsonSerializer`
305 */
306 T deserializeWithPolicy(Serializer, alias Policy, T, ARGS...)(ARGS args)
307 {
308 	auto deserializer = Serializer(args);
309 	return deserializeValueImpl!(Serializer, Policy).deserializeValue!T(deserializer);
310 }
311 
312 ///
313 unittest {
314 	import vibe.data.json;
315 
316 	template SizePol(T)
317 		if (__traits(allMembers, T) == TypeTuple!("x", "y"))
318 	{
319 		import std.conv;
320 		import std.array;
321 
322 		static string toRepresentation(T value)
323 		@safe {
324 			return to!string(value.x) ~ "x" ~ to!string(value.y);
325 		}
326 
327 		static T fromRepresentation(string value)
328 		@safe {
329 			string[] fields = value.split('x');
330 			alias fieldT = typeof(T.x);
331 			auto x = to!fieldT(fields[0]);
332 			auto y = to!fieldT(fields[1]);
333 			return T(x, y);
334 		}
335 	}
336 
337 	static struct SizeI {
338 		int x;
339 		int y;
340 	}
341 
342 	Json serializedI = "1x2";
343 	SizeI sizeI = deserializeWithPolicy!(JsonSerializer, SizePol, SizeI)(serializedI);
344 	assert(sizeI.x == 1);
345 	assert(sizeI.y == 2);
346 
347 	static struct SizeF {
348 		float x;
349 		float y;
350 	}
351 	Json serializedF = "0.1x0.2";
352 	SizeF sizeF = deserializeWithPolicy!(JsonSerializer, SizePol, SizeF)(serializedF);
353 	assert(sizeF.x == 0.1f);
354 	assert(sizeF.y == 0.2f);
355 }
356 
357 private template serializeValueImpl(Serializer, alias Policy) {
358 	alias _Policy = Policy;
359 	static assert(Serializer.isSupportedValueType!string, "All serializers must support string values.");
360 	static assert(Serializer.isSupportedValueType!(typeof(null)), "All serializers must support null values.");
361 
362 	// work around https://issues.dlang.org/show_bug.cgi?id=16528
363 	static if (isSafeSerializer!Serializer) {
364 		void serializeValue(T, ATTRIBUTES...)(ref Serializer ser, auto ref T value) @safe { serializeValueDeduced!(T, ATTRIBUTES)(ser, value); }
365 	} else {
366 		void serializeValue(T, ATTRIBUTES...)(ref Serializer ser, auto ref T value) { serializeValueDeduced!(T, ATTRIBUTES)(ser, value); }
367 	}
368 
369 	private void serializeValueDeduced(T, ATTRIBUTES...)(ref Serializer ser, auto ref T value)
370 	{
371 		import std.typecons : BitFlags, Nullable, Tuple, Typedef, TypedefType, tuple;
372 
373 		alias TU = Unqual!T;
374 
375 		alias Traits = .Traits!(TU, _Policy, ATTRIBUTES);
376 
377 		static if (isPolicySerializable!(Policy, TU)) {
378 			alias CustomType = typeof(Policy!TU.toRepresentation(TU.init));
379 			ser.serializeValue!(CustomType, ATTRIBUTES)(Policy!TU.toRepresentation(value));
380 		} else static if (is(TU == enum)) {
381 			static if (hasPolicyAttributeL!(ByNameAttribute, Policy, ATTRIBUTES)) {
382 				ser.serializeValue!(string)(value.to!string());
383 			} else {
384 				ser.serializeValue!(OriginalType!TU)(cast(OriginalType!TU)value);
385 			}
386 		} else static if (Serializer.isSupportedValueType!TU) {
387 			static if (is(TU == typeof(null))) ser.writeValue!Traits(null);
388 			else ser.writeValue!(Traits)(value);
389 		} else static if (/*isInstanceOf!(Tuple, TU)*/is(T == Tuple!TPS, TPS...)) {
390 			import std.algorithm.searching: all;
391 			static if (all!"!a.empty"([TU.fieldNames]) &&
392 					   !hasPolicyAttributeL!(AsArrayAttribute, Policy, ATTRIBUTES)) {
393 				static if (__traits(compiles, ser.beginWriteDictionary!TU(0))) {
394 					auto nfields = value.length;
395 					ser.beginWriteDictionary!Traits(nfields);
396 				} else {
397 					ser.beginWriteDictionary!Traits();
398 				}
399 				foreach (i, _; T.Types) {
400 					alias TV = typeof(value[i]);
401 					alias STraits = SubTraits!(Traits, TV);
402 					ser.beginWriteDictionaryEntry!STraits(underscoreStrip(TU.fieldNames[i]));
403 					ser.serializeValue!(TV, ATTRIBUTES)(value[i]);
404 					ser.endWriteDictionaryEntry!STraits(underscoreStrip(TU.fieldNames[i]));
405 				}
406 				static if (__traits(compiles, ser.endWriteDictionary!TU(0))) {
407 					ser.endWriteDictionary!Traits(nfields);
408 				} else {
409 					ser.endWriteDictionary!Traits();
410 				}
411 			} else static if (TU.Types.length == 1) {
412 				ser.serializeValue!(typeof(value[0]), ATTRIBUTES)(value[0]);
413 			} else {
414 				ser.beginWriteArray!Traits(value.length);
415 				foreach (i, _; T.Types) {
416 					alias TV = typeof(value[i]);
417 					alias STraits = SubTraits!(Traits, TV);
418 					ser.beginWriteArrayEntry!STraits(i);
419 					ser.serializeValue!(TV, ATTRIBUTES)(value[i]);
420 					ser.endWriteArrayEntry!STraits(i);
421 				}
422 				ser.endWriteArray!Traits();
423 			}
424 		} else static if (isArray!TU) {
425 			alias TV = typeof(value[0]);
426 			alias STraits = SubTraits!(Traits, TV);
427 			ser.beginWriteArray!Traits(value.length);
428 			foreach (i, ref el; value) {
429 				ser.beginWriteArrayEntry!STraits(i);
430 				ser.serializeValue!(TV, ATTRIBUTES)(el);
431 				ser.endWriteArrayEntry!STraits(i);
432 			}
433 			ser.endWriteArray!Traits();
434 		} else static if (isAssociativeArray!TU) {
435 			alias TK = KeyType!TU;
436 			alias TV = ValueType!TU;
437 			alias STraits = SubTraits!(Traits, TV);
438 
439 			static if (__traits(compiles, ser.beginWriteDictionary!TU(0))) {
440 				auto nfields = value.length;
441 				ser.beginWriteDictionary!Traits(nfields);
442 			} else {
443 				ser.beginWriteDictionary!Traits();
444 			}
445 			foreach (key, ref el; value) {
446 				string keyname;
447 				static if (is(TK : string)) keyname = key;
448 				else static if (is(TK : real) || is(TK : long) || is(TK == enum)) keyname = key.to!string;
449 				else static if (isStringSerializable!TK) keyname = key.toString();
450 				else static assert(false, "Associative array keys must be strings, numbers, enums, or have toString/fromString methods.");
451 				ser.beginWriteDictionaryEntry!STraits(keyname);
452 				ser.serializeValue!(TV, ATTRIBUTES)(el);
453 				ser.endWriteDictionaryEntry!STraits(keyname);
454 			}
455 			static if (__traits(compiles, ser.endWriteDictionary!TU(0))) {
456 				ser.endWriteDictionary!Traits(nfields);
457 			} else {
458 				ser.endWriteDictionary!Traits();
459 			}
460 		} else static if (/*isInstanceOf!(Nullable, TU)*/is(T == Nullable!TPS, TPS...)) {
461 			if (value.isNull()) ser.serializeValue!(typeof(null))(null);
462 			else ser.serializeValue!(typeof(value.get()), ATTRIBUTES)(value.get());
463 		} else static if (isInstanceOf!(Typedef, TU)) {
464 			ser.serializeValue!(TypedefType!TU, ATTRIBUTES)(cast(TypedefType!TU)value);
465 		} else static if (is(TU == BitFlags!E, E)) {
466 			alias STraits = SubTraits!(Traits, E);
467 
468 			size_t cnt = 0;
469 			foreach (v; EnumMembers!E)
470 				if (value & v)
471 					cnt++;
472 
473 			ser.beginWriteArray!Traits(cnt);
474 			cnt = 0;
475 			foreach (v; EnumMembers!E)
476 				if (value & v) {
477 					ser.beginWriteArrayEntry!STraits(cnt);
478 					ser.serializeValue!(E, ATTRIBUTES)(v);
479 					ser.endWriteArrayEntry!STraits(cnt);
480 					cnt++;
481 				}
482 			ser.endWriteArray!Traits();
483 		} else static if (isCustomSerializable!TU) {
484 			alias CustomType = typeof(T.init.toRepresentation());
485 			ser.serializeValue!(CustomType, ATTRIBUTES)(value.toRepresentation());
486 		} else static if (isISOExtStringSerializable!TU) {
487 			ser.serializeValue!(string, ATTRIBUTES)(value.toISOExtString());
488 		} else static if (isStringSinkSerializable!TU) {
489 			static if (doesSerializerSupportStringSink!Serializer) {
490 				ser.writeStringSinkValue!Traits(value);
491 			} else {
492 				import std.format : formattedWrite;
493 				auto app = appender!string;
494 				app.formattedWrite("%s", value);
495 				ser.serializeValue!(string, ATTRIBUTES)(app.data);
496 			}
497 		} else static if (isStringSerializable!TU) {
498 			ser.serializeValue!(string, ATTRIBUTES)(value.toString());
499 		} else static if (is(TU == struct) || is(TU == class)) {
500 			static if (!hasSerializableFields!(TU, Policy))
501 				pragma(msg, "Serializing composite type "~T.stringof~" which has no serializable fields");
502 			static if (is(TU == class)) {
503 				if (value is null) {
504 					ser.serializeValue!(typeof(null))(null);
505 					return;
506 				}
507 			}
508 			static auto safeGetMember(string mname)(ref T val) @safe {
509 				static if (__traits(compiles, __traits(getMember, val, mname))) {
510 					return __traits(getMember, val, mname);
511 				} else {
512 					pragma(msg, "Warning: Getter for "~fullyQualifiedName!T~"."~mname~" is not @safe");
513 					return () @trusted { return __traits(getMember, val, mname); } ();
514 				}
515 			}
516 			static if (hasPolicyAttributeL!(AsArrayAttribute, Policy, ATTRIBUTES)) {
517 				enum nfields = getExpandedFieldCount!(TU, SerializableFields!(TU, Policy));
518 				ser.beginWriteArray!Traits(nfields);
519 				size_t fcount = 0;
520 				foreach (mname; SerializableFields!(TU, Policy)) {
521 					alias TMS = TypeTuple!(typeof(__traits(getMember, value, mname)));
522 					foreach (j, TM; TMS) {
523 						alias TA = TypeTuple!(__traits(getAttributes, TypeTuple!(__traits(getMember, T, mname))[j]));
524 						alias STraits = SubTraits!(Traits, TM, TA);
525 						ser.beginWriteArrayEntry!STraits(fcount);
526 						static if (!isBuiltinTuple!(T, mname))
527 							ser.serializeValue!(TM, TA)(safeGetMember!mname(value));
528 						else
529 							ser.serializeValue!(TM, TA)(tuple(__traits(getMember, value, mname))[j]);
530 						ser.endWriteArrayEntry!STraits(fcount);
531 						fcount++;
532 					}
533 				}
534 				ser.endWriteArray!Traits();
535 			} else {
536 				static if (__traits(compiles, ser.beginWriteDictionary!Traits(0))) {
537 					auto nfields = getExpandedFieldCount!(TU, SerializableFields!(TU, Policy));
538 
539 					foreach (mname; SerializableFields!(TU, Policy)) {
540 						static if (!isBuiltinTuple!(T, mname)) {
541 							auto vt = safeGetMember!mname(value);
542 							static if (is(typeof(vt) : Nullable!NVT, NVT)
543 									&& hasPolicyAttribute!(EmbedNullableIgnoreNullAttribute, Policy, TypeTuple!(__traits(getMember, T, mname))[0])) {
544 								if (vt.isNull) nfields--;
545 							}
546 						}
547 					}
548 
549 					ser.beginWriteDictionary!Traits(nfields);
550 				} else {
551 					ser.beginWriteDictionary!Traits();
552 				}
553 				foreach (mname; SerializableFields!(TU, Policy)) {
554 					alias TM = TypeTuple!(typeof(__traits(getMember, TU, mname)));
555 					alias TA = TypeTuple!(__traits(getAttributes, TypeTuple!(__traits(getMember, T, mname))[0]));
556 					enum name = getPolicyAttribute!(TU, mname, NameAttribute, Policy)(NameAttribute!DefaultPolicy(underscoreStrip(mname))).name;
557 					static if (!isBuiltinTuple!(T, mname)) {
558 						auto vtn = safeGetMember!mname(value);
559 						static if (is(typeof(vtn) : Nullable!NVT, NVT)
560 								&& hasPolicyAttribute!(EmbedNullableIgnoreNullAttribute, Policy, TypeTuple!(__traits(getMember, T, mname))[0])) {
561 							if (vtn.isNull) continue;
562 							auto vt = vtn.get;
563 						} else {
564 							auto vt = vtn;
565 						}
566 					} else {
567 						alias TTM = TypeTuple!(typeof(__traits(getMember, value, mname)));
568 						auto vt = tuple!TTM(__traits(getMember, value, mname));
569 					}
570 					alias STraits = SubTraits!(Traits, typeof(vt), TA);
571 					ser.beginWriteDictionaryEntry!STraits(name);
572 					ser.serializeValue!(typeof(vt), TA)(vt);
573 					ser.endWriteDictionaryEntry!STraits(name);
574 				}
575 				static if (__traits(compiles, ser.endWriteDictionary!Traits(0))) {
576 					ser.endWriteDictionary!Traits(nfields);
577 				} else {
578 					ser.endWriteDictionary!Traits();
579 				}
580 			}
581 		} else static if (isPointer!TU) {
582 			if (value is null) {
583 				ser.writeValue!Traits(null);
584 				return;
585 			}
586 			ser.serializeValue!(PointerTarget!TU)(*value);
587 		} else static if (is(TU == bool) || is(TU : real) || is(TU : long)) {
588 			ser.serializeValue!(string, ATTRIBUTES)(to!string(value));
589 		} else static assert(false, "Unsupported serialization type: " ~ T.stringof);
590 	}
591 }
592 
593 ///
594 package template doesSerializerSupportStringSink(SerT)
595 {
596 	static struct T1 { void toString(scope void delegate(scope const(char)[])) {} }
597 	static struct T2 { void toString(R)(ref R dst) { dst.put('f'); dst.put("foo"); } }
598 
599 	enum doesSerializerSupportStringSink =
600 		is(typeof(SerT.init.writeStringSinkValue!(Traits!(T1, DefaultPolicy))(T1.init)))
601 		&& is(typeof(SerT.init.writeStringSinkValue!(Traits!(T2, DefaultPolicy))(T2.init)));
602 }
603 
604 ///
605 template isStringSinkSerializable(T)
606 {
607 	import std.range : nullSink;
608 
609 	private void sink(S : const(char)[])(scope S s) @safe {}
610 
611 	enum isStringSinkSerializable =
612 		(
613 			is(typeof(T.init.toString((scope str) => sink(str))))
614 			|| is(typeof(T.init.toString(nullSink)))
615 		)
616 		&& is(typeof(T.fromString(string.init)) : T);
617 }
618 
619 unittest {
620 	import std.array : split;
621 	import std.format : formattedWrite;
622 	import vibe.data.json;
623 
624 	static struct X(alias hasSink) {
625 		private int i;
626 		private string s;
627 
628 		static if (hasSink) {
629 			void toString (scope void delegate(scope const(char)[]) @safe dg) @safe
630 			{
631 				formattedWrite(dg, "%d;%s", this.i, this.s);
632 			}
633 		}
634 
635 		string toString () @safe const pure nothrow
636 		{
637 			return "42;hello";
638 		}
639 
640 		static X fromString (string s) @safe pure
641 		{
642 			auto parts = s.split(";");
643 			auto x = X(parts[0].to!int, parts[1]);
644 			return x;
645 		}
646 	}
647 
648 	static assert(!isStringSinkSerializable!(X!false));
649 	static assert(isStringSinkSerializable!(X!true));
650 
651 	// old toString() style methods still work if no sink overload presented
652 	auto serialized1 = X!false(7,"x1").serializeToJsonString();
653 	assert(serialized1 == `"42;hello"`);
654 	auto deserialized1 = deserializeJson!(X!false)(serialized1);
655 	assert(deserialized1.i == 42);
656 	assert(deserialized1.s == "hello");
657 
658 	// sink overload takes precedence
659 	auto serialized2 = X!true(7,"x2").serializeToJsonString();
660 	assert(serialized2 == `"7;x2"`);
661 	auto deserialized2 = deserializeJson!(X!true)(serialized2);
662 	assert(deserialized2.i == 7);
663 	assert(deserialized2.s == "x2");
664 
665 	// type is sink serializable, but serializer doesn't support sink
666 	auto serialized3 = X!true(7,"x2").serializeToJson();
667 	assert(to!string(serialized3) == `"7;x2"`);
668 	auto deserialized3 = deserializeJson!(X!true)(serialized3);
669 	assert(deserialized3.i == 7);
670 	assert(deserialized3.s == "x2");
671 }
672 
673 private struct Traits(T, alias POL, ATTRIBUTES...)
674 {
675 	alias Type = T;
676 	alias Policy = POL;
677 	alias Attributes = TypeTuple!ATTRIBUTES;
678 }
679 
680 private struct SubTraits(Traits, T, A...)
681 {
682 	alias Type = Unqual!T;
683 	alias Attributes = TypeTuple!A;
684 	alias Policy = Traits.Policy;
685 	alias ContainerType = Traits.Type;
686 	alias ContainerAttributes = Traits.Attributes;
687 }
688 
689 private template deserializeValueImpl(Serializer, alias Policy) {
690 	alias _Policy = Policy;
691 	static assert(Serializer.isSupportedValueType!string, "All serializers must support string values.");
692 	static assert(Serializer.isSupportedValueType!(typeof(null)), "All serializers must support null values.");
693 
694 	// work around https://issues.dlang.org/show_bug.cgi?id=16528
695 	static if (isSafeDeserializer!Serializer) {
696 		T deserializeValue(T, ATTRIBUTES...)(ref Serializer ser) @safe { return deserializeValueDeduced!(T, ATTRIBUTES)(ser); }
697 	} else {
698 		T deserializeValue(T, ATTRIBUTES...)(ref Serializer ser) { return deserializeValueDeduced!(T, ATTRIBUTES)(ser); }
699 	}
700 
701 	T deserializeValueDeduced(T, ATTRIBUTES...)(ref Serializer ser) if(!isMutable!T)
702 	{
703 		import std.algorithm.mutation : move;
704 		auto ret = deserializeValue!(Unqual!T, ATTRIBUTES)(ser);
705 		return () @trusted { return cast(T)ret.move; } ();
706 	}
707 
708 	T deserializeValueDeduced(T, ATTRIBUTES...)(ref Serializer ser) if(isMutable!T)
709 	{
710 		import std.typecons : BitFlags, Nullable, Typedef, TypedefType, Tuple;
711 
712 		alias Traits = .Traits!(T, _Policy, ATTRIBUTES);
713 
714 		static if (isPolicySerializable!(Policy, T)) {
715 			alias CustomType = typeof(Policy!T.toRepresentation(T.init));
716 			return Policy!T.fromRepresentation(ser.deserializeValue!(CustomType, ATTRIBUTES));
717 		} else static if (is(T == enum)) {
718 			static if (hasPolicyAttributeL!(ByNameAttribute, Policy, ATTRIBUTES)) {
719 				return ser.deserializeValue!(string, ATTRIBUTES).to!T();
720 			} else {
721 				return cast(T)ser.deserializeValue!(OriginalType!T);
722 			}
723 		} else static if (Serializer.isSupportedValueType!T) {
724 			return ser.readValue!(Traits, T)();
725 		} else static if (/*isInstanceOf!(Tuple, TU)*/is(T == Tuple!TPS, TPS...)) {
726 			enum fieldsCount = T.Types.length;
727 			import std.algorithm.searching: all;
728 			static if (all!"!a.empty"([T.fieldNames]) &&
729 					   !hasPolicyAttributeL!(AsArrayAttribute, Policy, ATTRIBUTES)) {
730 				T ret;
731 				bool[fieldsCount] set;
732 				ser.readDictionary!Traits((name) {
733 					switch (name) {
734 						default:
735 							static if (is(typeof(ser.skipValue()))) {
736 								ser.skipValue();
737 							}
738 							break;
739 						foreach (i, TV; T.Types) {
740 							enum fieldName = underscoreStrip(T.fieldNames[i]);
741 							alias STraits = SubTraits!(Traits, TV);
742 							case fieldName: {
743 								ser.beginReadDictionaryEntry!STraits(fieldName);
744 								ret[i] = ser.deserializeValue!(TV, ATTRIBUTES);
745 								ser.endReadDictionaryEntry!STraits(fieldName);
746 								set[i] = true;
747 							} break;
748 						}
749 					}
750 				});
751 				foreach (i, fieldName; T.fieldNames)
752 					enforce(set[i], "Missing tuple field '"~fieldName~"' of type '"~T.Types[i].stringof~"' ("~Policy.stringof~").");
753 				return ret;
754 			} else static if (fieldsCount == 1) {
755 				return T(ser.deserializeValue!(T.Types[0], ATTRIBUTES)());
756 			} else {
757 				T ret;
758 				size_t currentField = 0;
759 				ser.readArray!Traits((sz) { assert(sz == 0 || sz == fieldsCount); }, {
760 					switch (currentField++) {
761 						default: break;
762 						foreach (i, TV; T.Types) {
763 							alias STraits = SubTraits!(Traits, TV);
764 							case i: {
765 								ser.beginReadArrayEntry!STraits(i);
766 								ret[i] = ser.deserializeValue!(TV, ATTRIBUTES);
767 								ser.endReadArrayEntry!STraits(i);
768 							} break;
769 						}
770 					}
771 				});
772 				enforce(currentField == fieldsCount, "Missing tuple field(s) - expected '"~fieldsCount.stringof~"', received '"~currentField.stringof~"' ("~Policy.stringof~").");
773 				return ret;
774 			}
775 		} else static if (isStaticArray!T) {
776 			alias TV = typeof(T.init[0]);
777 			alias STraits = SubTraits!(Traits, TV);
778 			T ret;
779 			size_t i = 0;
780 			ser.readArray!Traits((sz) { assert(sz == 0 || sz == T.length); }, {
781 				assert(i < T.length);
782 				ser.beginReadArrayEntry!STraits(i);
783 				ret[i] = ser.deserializeValue!(TV, ATTRIBUTES);
784 				ser.endReadArrayEntry!STraits(i);
785 				i++;
786 			});
787 			return ret;
788 		} else static if (isDynamicArray!T) {
789 			alias TV = typeof(T.init[0]);
790 			alias STraits = SubTraits!(Traits, TV);
791 			//auto ret = appender!T();
792 			T ret; // Cannot use appender because of DMD BUG 10690/10859/11357
793 			ser.readArray!Traits((sz) @safe { ret.reserve(sz); }, () @safe {
794 				size_t i = ret.length;
795 				ser.beginReadArrayEntry!STraits(i);
796 				static if (__traits(compiles, () @safe { ser.deserializeValue!(TV, ATTRIBUTES); }))
797 					ret ~= ser.deserializeValue!(TV, ATTRIBUTES);
798 				else // recursive array https://issues.dlang.org/show_bug.cgi?id=16528
799 					ret ~= (() @trusted => ser.deserializeValue!(TV, ATTRIBUTES))();
800 				ser.endReadArrayEntry!STraits(i);
801 			});
802 			return ret;//cast(T)ret.data;
803 		} else static if (isAssociativeArray!T) {
804 			alias TK = KeyType!T;
805 			alias TV = ValueType!T;
806 			alias STraits = SubTraits!(Traits, TV);
807 
808 			T ret;
809 			ser.readDictionary!Traits((name) @safe {
810 				TK key;
811 				static if (is(TK == string) || (is(TK == enum) && is(OriginalType!TK == string))) key = cast(TK)name;
812 				else static if (is(TK : real) || is(TK : long) || is(TK == enum)) key = name.to!TK;
813 				else static if (isStringSerializable!TK) key = TK.fromString(name);
814 				else static assert(false, "Associative array keys must be strings, numbers, enums, or have toString/fromString methods.");
815 				ser.beginReadDictionaryEntry!STraits(name);
816 				ret[key] = ser.deserializeValue!(TV, ATTRIBUTES);
817 				ser.endReadDictionaryEntry!STraits(name);
818 			});
819 			return ret;
820 		} else static if (isInstanceOf!(Nullable, T)) {
821 			if (ser.tryReadNull!Traits()) return T.init;
822 			return T(ser.deserializeValue!(typeof(T.init.get()), ATTRIBUTES));
823 		} else static if (isInstanceOf!(Typedef, T)) {
824 			return T(ser.deserializeValue!(TypedefType!T, ATTRIBUTES));
825 		} else static if (is(T == BitFlags!E, E)) {
826 			alias STraits = SubTraits!(Traits, E);
827 			T ret;
828 			size_t i = 0;
829 			ser.readArray!Traits((sz) {}, {
830 				ser.beginReadArrayEntry!STraits(i);
831 				ret |= ser.deserializeValue!(E, ATTRIBUTES);
832 				ser.endReadArrayEntry!STraits(i);
833 				i++;
834 			});
835 			return ret;
836 		} else static if (isCustomSerializable!T) {
837 			alias CustomType = typeof(T.init.toRepresentation());
838 			return T.fromRepresentation(ser.deserializeValue!(CustomType, ATTRIBUTES));
839 		} else static if (isISOExtStringSerializable!T) {
840 			return T.fromISOExtString(ser.readValue!(Traits, string)());
841 		} else static if (isStringSerializable!T) {
842 			return T.fromString(ser.readValue!(Traits, string)());
843 		} else static if (is(T == struct) || is(T == class)) {
844 			static if (is(T == class)) {
845 				if (ser.tryReadNull!Traits()) return null;
846 			}
847 
848 			T ret;
849 			string name;
850 			bool[getExpandedFieldsData!(T, SerializableFields!(T, Policy)).length] set;
851 			static if (is(T == class)) ret = new T;
852 
853 			void safeSetMember(string mname, U)(ref T value, U fval)
854 			@safe {
855 				static if (__traits(compiles, () @safe { __traits(getMember, value, mname) = fval; }))
856 					__traits(getMember, value, mname) = fval;
857 				else {
858 					pragma(msg, "Warning: Setter for "~fullyQualifiedName!T~"."~mname~" is not @safe");
859 					() @trusted { __traits(getMember, value, mname) = fval; } ();
860 				}
861 			}
862 
863 			static if (hasPolicyAttributeL!(AsArrayAttribute, Policy, ATTRIBUTES)) {
864 				size_t idx = 0;
865 				ser.readArray!Traits((sz){}, {
866 					static if (hasSerializableFields!(T, Policy)) {
867 						switch (idx++) {
868 							default: break;
869 							foreach (i, FD; getExpandedFieldsData!(T, SerializableFields!(T, Policy))) {
870 								enum mname = FD[0];
871 								enum msindex = FD[1];
872 								alias MT = TypeTuple!(__traits(getMember, T, mname));
873 								alias MTI = MT[msindex];
874 								alias TMTI = typeof(MTI);
875 								alias TMTIA = TypeTuple!(__traits(getAttributes, MTI));
876 								alias STraits = SubTraits!(Traits, TMTI, TMTIA);
877 
878 							case i:
879 								static if (hasPolicyAttribute!(OptionalAttribute, Policy, MTI))
880 									if (ser.tryReadNull!STraits()) return;
881 								set[i] = true;
882 								ser.beginReadArrayEntry!STraits(i);
883 								static if (!isBuiltinTuple!(T, mname)) {
884 									safeSetMember!mname(ret, ser.deserializeValue!(TMTI, TMTIA));
885 								} else {
886 									__traits(getMember, ret, mname)[msindex] = ser.deserializeValue!(TMTI, TMTIA);
887 								}
888 								ser.endReadArrayEntry!STraits(i);
889 								break;
890 							}
891 						}
892 					} else {
893 						pragma(msg, "Deserializing composite type "~T.stringof~" which has no serializable fields.");
894 					}
895 				});
896 			} else {
897 				ser.readDictionary!Traits((name) {
898 					static if (hasSerializableFields!(T, Policy)) {
899 						switch (name) {
900 							default:
901 								static if (is(typeof(ser.skipValue()))) {
902 									ser.skipValue();
903 								}
904 								break;
905 							foreach (i, mname; SerializableFields!(T, Policy)) {
906 								alias TM = TypeTuple!(typeof(__traits(getMember, T, mname)));
907 								alias TA = TypeTuple!(__traits(getAttributes, TypeTuple!(__traits(getMember, T, mname))[0]));
908 								alias STraits = SubTraits!(Traits, TM, TA);
909 								enum fname = getPolicyAttribute!(T, mname, NameAttribute, Policy)(NameAttribute!DefaultPolicy(underscoreStrip(mname))).name;
910 								case fname:
911 									static if (hasPolicyAttribute!(OptionalAttribute, Policy, TypeTuple!(__traits(getMember, T, mname))[0]))
912 										if (ser.tryReadNull!STraits()) return;
913 									set[i] = true;
914 									ser.beginReadDictionaryEntry!STraits(fname);
915 									static if (!isBuiltinTuple!(T, mname)) {
916 										safeSetMember!mname(ret, ser.deserializeValue!(TM, TA));
917 									} else {
918 										__traits(getMember, ret, mname) = ser.deserializeValue!(Tuple!TM, TA);
919 									}
920 									ser.endReadDictionaryEntry!STraits(fname);
921 									break;
922 							}
923 						}
924 					} else {
925 						pragma(msg, "Deserializing composite type "~T.stringof~" which has no serializable fields.");
926 					}
927 				});
928 			}
929 			foreach (i, mname; SerializableFields!(T, Policy))
930 				static if (!hasPolicyAttribute!(OptionalAttribute, Policy, TypeTuple!(__traits(getMember, T, mname))[0]))
931 					enforce(set[i], "Missing non-optional field '"~mname~"' of type '"~T.stringof~"' ("~Policy.stringof~").");
932 			return ret;
933 		} else static if (isPointer!T) {
934 			if (ser.tryReadNull!Traits()) return null;
935 			alias PT = PointerTarget!T;
936 			auto ret = new PT;
937 			*ret = ser.deserializeValue!(PT, ATTRIBUTES);
938 			return ret;
939 		} else static if (is(T == bool) || is(T : real) || is(T : long)) {
940 			return to!T(ser.deserializeValue!string());
941 		} else static assert(false, "Unsupported serialization type: " ~ T.stringof);
942 	}
943 }
944 
945 
946 /**
947 	Attribute for overriding the field name during (de-)serialization.
948 
949 	Note that without the `@name` attribute there is a shorter alternative
950 	for using names that collide with a D keyword. A single trailing
951 	underscore will automatically be stripped when determining a field
952 	name.
953 */
954 NameAttribute!Policy name(alias Policy = DefaultPolicy)(string name)
955 {
956 	return NameAttribute!Policy(name);
957 }
958 ///
959 unittest {
960 	struct CustomPolicy {}
961 
962 	struct Test {
963 		// serialized as "screen-size":
964 		@name("screen-size") int screenSize;
965 
966 		// serialized as "print-size" by default,
967 		// but as "PRINTSIZE" if CustomPolicy is used for serialization.
968 		@name("print-size")
969 		@name!CustomPolicy("PRINTSIZE")
970 		int printSize;
971 
972 		// serialized as "version"
973 		int version_;
974 	}
975 }
976 
977 
978 /**
979 	Attribute marking a field as optional during deserialization.
980 */
981 @property OptionalAttribute!Policy optional(alias Policy = DefaultPolicy)()
982 {
983 	return OptionalAttribute!Policy();
984 }
985 ///
986 unittest {
987 	struct Test {
988 		// does not need to be present during deserialization
989 		@optional int screenSize = 100;
990 	}
991 }
992 
993 
994 /**
995 	Attribute for marking non-serialized fields.
996 */
997 @property IgnoreAttribute!Policy ignore(alias Policy = DefaultPolicy)()
998 {
999 	return IgnoreAttribute!Policy();
1000 }
1001 ///
1002 unittest {
1003 	struct Test {
1004 		// is neither serialized not deserialized
1005 		@ignore int screenSize;
1006 	}
1007 }
1008 ///
1009 unittest {
1010 	template CustomPolicy(T) {
1011 		// ...
1012 	}
1013 
1014 	struct Test {
1015 		// not (de)serialized for serializeWithPolicy!(Test, CustomPolicy)
1016 		// but for other policies or when serialized without a policy
1017 		@ignore!CustomPolicy int screenSize;
1018 	}
1019 }
1020 
1021 
1022 /**
1023 	Attribute for forcing serialization of enum fields by name instead of by value.
1024 */
1025 @property ByNameAttribute!Policy byName(alias Policy = DefaultPolicy)()
1026 {
1027 	return ByNameAttribute!Policy();
1028 }
1029 ///
1030 unittest {
1031 	enum Color {
1032 		red,
1033 		green,
1034 		blue
1035 	}
1036 
1037 	struct Test {
1038 		// serialized as an int (e.g. 1 for Color.green)
1039 		Color color;
1040 		// serialized as a string (e.g. "green" for Color.green)
1041 		@byName Color namedColor;
1042 		// serialized as array of ints
1043 		Color[] colorArray;
1044 		// serialized as array of strings
1045 		@byName Color[] namedColorArray;
1046 	}
1047 }
1048 
1049 
1050 /**
1051 	Attribute for representing a struct/class as an array instead of an object.
1052 
1053 	Usually structs and class objects are serialized as dictionaries mapping
1054 	from field name to value. Using this attribute, they will be serialized
1055 	as a flat array instead. Note that changing the layout will make any
1056 	already serialized data mismatch when this attribute is used.
1057 */
1058 @property AsArrayAttribute!Policy asArray(alias Policy = DefaultPolicy)()
1059 {
1060 	return AsArrayAttribute!Policy();
1061 }
1062 ///
1063 unittest {
1064 	struct Fields {
1065 		int f1;
1066 		string f2;
1067 		double f3;
1068 	}
1069 
1070 	struct Test {
1071 		// serialized as name:value pairs ["f1": int, "f2": string, "f3": double]
1072 		Fields object;
1073 		// serialized as a sequential list of values [int, string, double]
1074 		@asArray Fields array;
1075 	}
1076 
1077 	import vibe.data.json;
1078 	static assert(is(typeof(serializeToJson(Test()))));
1079 }
1080 
1081 
1082 /**
1083 	Makes this nullable as if it is not a nullable to the serializer. Ignores the field completely when it is null.
1084 
1085 	Works with Nullable!classes and Nullable!structs. Behavior is undefined if this is applied to other types.
1086 
1087 	Implicitly marks this as optional for deserialization. (Keeps the struct default value when not present in serialized value)
1088 */
1089 @property EmbedNullableIgnoreNullAttribute!Policy embedNullable(alias Policy = DefaultPolicy)()
1090 {
1091 	return EmbedNullableIgnoreNullAttribute!Policy();
1092 }
1093 ///
1094 unittest {
1095 	import std.typecons : Nullable;
1096 
1097 	struct Test {
1098 		// Not serialized at all if null, ignored on deserialization if not present.
1099 		@embedNullable Nullable!int field;
1100 	}
1101 }
1102 
1103 
1104 ///
1105 enum FieldExistence
1106 {
1107 	missing,
1108 	exists,
1109 	defer
1110 }
1111 
1112 /// User defined attribute (not intended for direct use)
1113 struct NameAttribute(alias POLICY) { alias Policy = POLICY; string name; }
1114 /// ditto
1115 struct OptionalAttribute(alias POLICY) { alias Policy = POLICY; }
1116 /// ditto
1117 struct IgnoreAttribute(alias POLICY) { alias Policy = POLICY; }
1118 /// ditto
1119 struct ByNameAttribute(alias POLICY) { alias Policy = POLICY; }
1120 /// ditto
1121 struct AsArrayAttribute(alias POLICY) { alias Policy = POLICY; }
1122 /// ditto
1123 struct EmbedNullableIgnoreNullAttribute(alias POLICY) { alias Policy = POLICY; }
1124 
1125 /**
1126 	Checks if a given type has a custom serialization representation.
1127 
1128 	A class or struct type is custom serializable if it defines a pair of
1129 	`toRepresentation`/`fromRepresentation` methods. Any class or
1130 	struct type that has this trait will be serialized by using the return
1131 	value of it's `toRepresentation` method instead of the original value.
1132 
1133 	This trait has precedence over `isISOExtStringSerializable` and
1134 	`isStringSerializable`.
1135 */
1136 template isCustomSerializable(T)
1137 {
1138 	enum bool isCustomSerializable = is(typeof(T.init.toRepresentation())) && is(typeof(T.fromRepresentation(T.init.toRepresentation())) == T);
1139 }
1140 ///
1141 unittest {
1142 	// represented as a single uint when serialized
1143 	static struct S {
1144 		ushort x, y;
1145 
1146 		uint toRepresentation() const { return x + (y << 16); }
1147 		static S fromRepresentation(uint i) { return S(i & 0xFFFF, i >> 16); }
1148 	}
1149 
1150 	static assert(isCustomSerializable!S);
1151 }
1152 
1153 
1154 /**
1155 	Checks if a given type has an ISO extended string serialization representation.
1156 
1157 	A class or struct type is ISO extended string serializable if it defines a
1158 	pair of `toISOExtString`/`fromISOExtString` methods. Any class or
1159 	struct type that has this trait will be serialized by using the return
1160 	value of it's `toISOExtString` method instead of the original value.
1161 
1162 	This is mainly useful for supporting serialization of the the date/time
1163 	types in `std.datetime`.
1164 
1165 	This trait has precedence over `isStringSerializable`.
1166 */
1167 template isISOExtStringSerializable(T)
1168 {
1169 	enum bool isISOExtStringSerializable = is(typeof(T.init.toISOExtString()) : string) && is(typeof(T.fromISOExtString("")) : T);
1170 }
1171 ///
1172 unittest {
1173 	import std.datetime;
1174 
1175 	static assert(isISOExtStringSerializable!DateTime);
1176 	static assert(isISOExtStringSerializable!SysTime);
1177 
1178 	// represented as an ISO extended string when serialized
1179 	static struct S {
1180 		// dummy example implementations
1181 		string toISOExtString() const { return ""; }
1182 		static S fromISOExtString(string s) { return S.init; }
1183 	}
1184 
1185 	static assert(isISOExtStringSerializable!S);
1186 }
1187 
1188 
1189 /**
1190 	Checks if a given type has a string serialization representation.
1191 
1192 	A class or struct type is string serializable if it defines a pair of
1193 	`toString`/`fromString` methods. Any class or struct type that
1194 	has this trait will be serialized by using the return value of it's
1195 	`toString` method instead of the original value.
1196 */
1197 template isStringSerializable(T)
1198 {
1199 	enum bool isStringSerializable = is(typeof(T.init.toString()) : string) && is(typeof(T.fromString("")) : T);
1200 }
1201 ///
1202 unittest {
1203 	import std.conv;
1204 
1205 	// represented as a string when serialized
1206 	static struct S {
1207 		int value;
1208 
1209 		// dummy example implementations
1210 		string toString() const { return value.to!string(); }
1211 		static S fromString(string s) { return S(s.to!int()); }
1212 	}
1213 
1214 	static assert(isStringSerializable!S);
1215 }
1216 
1217 
1218 /** Default policy (performs no customization).
1219 */
1220 template DefaultPolicy(T)
1221 {
1222 }
1223 
1224 /**
1225 	Checks if a given policy supports custom serialization for a given type.
1226 
1227 	A class or struct type is custom serializable according to a policy if
1228 	the policy defines a pair of `toRepresentation`/`fromRepresentation`
1229 	functions. Any class or struct type that has this trait for the policy supplied to
1230 	`serializeWithPolicy` will be serialized by using the return value of the
1231 	policy `toRepresentation` function instead of the original value.
1232 
1233 	This trait has precedence over `isCustomSerializable`,
1234 	`isISOExtStringSerializable` and `isStringSerializable`.
1235 
1236 	See_Also: `vibe.data.serialization.serializeWithPolicy`
1237 */
1238 template isPolicySerializable(alias Policy, T)
1239 {
1240 	enum bool isPolicySerializable = is(typeof(Policy!T.toRepresentation(T.init))) &&
1241 		is(typeof(Policy!T.fromRepresentation(Policy!T.toRepresentation(T.init))) : T);
1242 }
1243 ///
1244 unittest {
1245 	import std.conv;
1246 
1247 	// represented as the boxed value when serialized
1248 	static struct Box(T) {
1249 		T value;
1250 	}
1251 
1252 	template BoxPol(S)
1253 	{
1254 		auto toRepresentation(S s) {
1255 			return s.value;
1256 		}
1257 
1258 		S fromRepresentation(typeof(S.init.value) v) {
1259 			return S(v);
1260 		}
1261 	}
1262 	static assert(isPolicySerializable!(BoxPol, Box!int));
1263 }
1264 
1265 
1266 /**
1267 	Chains serialization policy.
1268 
1269 	Constructs a serialization policy that given a type `T` will apply the
1270 	first compatible policy `toRepresentation` and `fromRepresentation`
1271 	functions. Policies are evaluated left-to-right according to
1272 	`isPolicySerializable`.
1273 
1274 	See_Also: `vibe.data.serialization.serializeWithPolicy`
1275 */
1276 template ChainedPolicy(alias Primary, Fallbacks...)
1277 {
1278 	static if (Fallbacks.length == 0) {
1279 		alias ChainedPolicy = Primary;
1280 	} else {
1281 		alias ChainedPolicy = ChainedPolicy!(ChainedPolicyImpl!(Primary, Fallbacks[0]), Fallbacks[1..$]);
1282 	}
1283 }
1284 ///
1285 unittest {
1286 	import std.conv;
1287 
1288 	// To be represented as the boxed value when serialized
1289 	static struct Box(T) {
1290 		T value;
1291 	}
1292 	// Also to berepresented as the boxed value when serialized, but has
1293 	// a different way to access the value.
1294 	static struct Box2(T) {
1295 		private T v;
1296 		ref T get() {
1297 			return v;
1298 		}
1299 	}
1300 	template BoxPol(S)
1301 	{
1302 		auto toRepresentation(S s) {
1303 			return s.value;
1304 		}
1305 
1306 		S fromRepresentation(typeof(toRepresentation(S.init)) v) {
1307 			return S(v);
1308 		}
1309 	}
1310 	template Box2Pol(S)
1311 	{
1312 		auto toRepresentation(S s) {
1313 			return s.get();
1314 		}
1315 
1316 		S fromRepresentation(typeof(toRepresentation(S.init)) v) {
1317 			S s;
1318 			s.get() = v;
1319 			return s;
1320 		}
1321 	}
1322 	alias ChainPol = ChainedPolicy!(BoxPol, Box2Pol);
1323 	static assert(!isPolicySerializable!(BoxPol, Box2!int));
1324 	static assert(!isPolicySerializable!(Box2Pol, Box!int));
1325 	static assert(isPolicySerializable!(ChainPol, Box!int));
1326 	static assert(isPolicySerializable!(ChainPol, Box2!int));
1327 }
1328 
1329 private template ChainedPolicyImpl(alias Primary, alias Fallback)
1330 {
1331 	template Pol(T)
1332 	{
1333 		static if (isPolicySerializable!(Primary, T)) {
1334 			alias toRepresentation = Primary!T.toRepresentation;
1335 			alias fromRepresentation = Primary!T.fromRepresentation;
1336 		} else {
1337 			alias toRepresentation = Fallback!T.toRepresentation;
1338 			alias fromRepresentation = Fallback!T.fromRepresentation;
1339 		}
1340 	}
1341 	alias ChainedPolicyImpl = Pol;
1342 }
1343 
1344 private template isBuiltinTuple(T, string member)
1345 {
1346     alias TM = AliasSeq!(typeof(__traits(getMember, T.init, member)));
1347     static if (TM.length > 1) enum isBuiltinTuple = true;
1348     else static if (is(typeof(__traits(getMember, T.init, member)) == TM[0]))
1349         enum isBuiltinTuple = false;
1350     else enum isBuiltinTuple = true; // single-element tuple
1351 }
1352 
1353 // heuristically determines @safe'ty of the serializer by testing readValue and writeValue for type int
1354 private template isSafeSerializer(S)
1355 {
1356 	alias T = Traits!(int, DefaultPolicy);
1357 	static if (__traits(hasMember, S, "writeValue"))
1358 		enum isSafeSerializer = __traits(compiles, (S s) @safe { s.writeValue!T(42); });
1359 	else static assert(0, "Serializer is missing required writeValue method");
1360 }
1361 
1362 // heuristically determines @safe'ty of the deserializer by testing readValue and writeValue for type int
1363 private template isSafeDeserializer(S)
1364 {
1365 	alias T = Traits!(int, DefaultPolicy);
1366 	static if (__traits(hasMember, S, "readValue"))
1367 		enum isSafeDeserializer = __traits(compiles, (S s) @safe { s.readValue!(T, int)(); });
1368 	else static assert(0, "Deserializer is missing required readValue method");
1369 }
1370 
1371 private template hasAttribute(T, alias decl) { enum hasAttribute = findFirstUDA!(T, decl).found; }
1372 
1373 unittest {
1374 	@asArray int i1;
1375 	static assert(hasAttribute!(AsArrayAttribute!DefaultPolicy, i1));
1376 	int i2;
1377 	static assert(!hasAttribute!(AsArrayAttribute!DefaultPolicy, i2));
1378 }
1379 
1380 private template hasPolicyAttribute(alias T, alias POLICY, alias decl)
1381 {
1382 	// __traits(identifier) to hack around T being a template and not a type
1383 	// this if makes hasPolicyAttribute!(OptionalAttribute) == true when EmbedNullableIgnoreNullAttribute is present.
1384 	static if (__traits(identifier, T) == __traits(identifier, OptionalAttribute))
1385 		enum hasPolicyAttribute = hasPolicyAttributeImpl!(T, POLICY, decl)
1386 			|| hasPolicyAttributeImpl!(EmbedNullableIgnoreNullAttribute, POLICY, decl);
1387 	else
1388 		enum hasPolicyAttribute = hasPolicyAttributeImpl!(T, POLICY, decl);
1389 }
1390 
1391 private template hasPolicyAttributeImpl(alias T, alias POLICY, alias decl)
1392 {
1393 	enum hasPolicyAttributeImpl = hasAttribute!(T!POLICY, decl) || hasAttribute!(T!DefaultPolicy, decl);
1394 }
1395 
1396 unittest {
1397 	import std.typecons : Nullable;
1398 
1399 	template CP(T) {}
1400 	@asArray!CP int i1;
1401 	@asArray int i2;
1402 	int i3;
1403 	@embedNullable Nullable!int i4;
1404 
1405 	static assert(hasPolicyAttribute!(AsArrayAttribute, CP, i1));
1406 	static assert(hasPolicyAttribute!(AsArrayAttribute, CP, i2));
1407 	static assert(!hasPolicyAttribute!(AsArrayAttribute, CP, i3));
1408 	static assert(!hasPolicyAttribute!(AsArrayAttribute, DefaultPolicy, i1));
1409 	static assert(hasPolicyAttribute!(AsArrayAttribute, DefaultPolicy, i2));
1410 	static assert(!hasPolicyAttribute!(AsArrayAttribute, DefaultPolicy, i3));
1411 	static assert(hasPolicyAttribute!(EmbedNullableIgnoreNullAttribute, DefaultPolicy, i4));
1412 	static assert(hasPolicyAttribute!(OptionalAttribute, DefaultPolicy, i4));
1413 	static assert(!hasPolicyAttribute!(IgnoreAttribute, DefaultPolicy, i4));
1414 }
1415 
1416 
1417 private template hasAttributeL(T, ATTRIBUTES...) {
1418 	static if (ATTRIBUTES.length == 1) {
1419 		enum hasAttributeL = is(typeof(ATTRIBUTES[0]) == T);
1420 	} else static if (ATTRIBUTES.length > 1) {
1421 		enum hasAttributeL = hasAttributeL!(T, ATTRIBUTES[0 .. $/2]) || hasAttributeL!(T, ATTRIBUTES[$/2 .. $]);
1422 	} else {
1423 		enum hasAttributeL = false;
1424 	}
1425 }
1426 
1427 unittest {
1428 	static assert(hasAttributeL!(AsArrayAttribute!DefaultPolicy, byName, asArray));
1429 	static assert(!hasAttributeL!(AsArrayAttribute!DefaultPolicy, byName));
1430 }
1431 
1432 private template hasPolicyAttributeL(alias T, alias POLICY, ATTRIBUTES...)
1433 {
1434 	enum hasPolicyAttributeL = hasAttributeL!(T!POLICY, ATTRIBUTES) || hasAttributeL!(T!DefaultPolicy, ATTRIBUTES);
1435 }
1436 
1437 private static T getAttribute(TT, string mname, T)(T default_value)
1438 {
1439 	enum val = findFirstUDA!(T, __traits(getMember, TT, mname));
1440 	static if (val.found) return val.value;
1441 	else return default_value;
1442 }
1443 
1444 private static auto getPolicyAttribute(TT, string mname, alias Attribute, alias Policy)(Attribute!DefaultPolicy default_value)
1445 {
1446 	enum val = findFirstUDA!(Attribute!Policy, TypeTuple!(__traits(getMember, TT, mname))[0]);
1447 	static if (val.found) return val.value;
1448 	else {
1449 		enum val2 = findFirstUDA!(Attribute!DefaultPolicy, TypeTuple!(__traits(getMember, TT, mname))[0]);
1450 		static if (val2.found) return val2.value;
1451 		else return default_value;
1452 	}
1453 }
1454 
1455 private string underscoreStrip(string field_name)
1456 @safe nothrow @nogc {
1457 	if( field_name.length < 1 || field_name[$-1] != '_' ) return field_name;
1458 	else return field_name[0 .. $-1];
1459 }
1460 
1461 
1462 private template hasSerializableFields(T, alias POLICY, size_t idx = 0)
1463 {
1464 	enum hasSerializableFields = SerializableFields!(T, POLICY).length > 0;
1465 	/*static if (idx < __traits(allMembers, T).length) {
1466 		enum mname = __traits(allMembers, T)[idx];
1467 		static if (!isRWPlainField!(T, mname) && !isRWField!(T, mname)) enum hasSerializableFields = hasSerializableFields!(T, idx+1);
1468 		else static if (hasAttribute!(IgnoreAttribute, __traits(getMember, T, mname))) enum hasSerializableFields = hasSerializableFields!(T, idx+1);
1469 		else enum hasSerializableFields = true;
1470 	} else enum hasSerializableFields = false;*/
1471 }
1472 
1473 private template SerializableFields(COMPOSITE, alias POLICY)
1474 {
1475 	alias SerializableFields = FilterSerializableFields!(COMPOSITE, POLICY, __traits(allMembers, COMPOSITE));
1476 }
1477 
1478 private template FilterSerializableFields(COMPOSITE, alias POLICY, FIELDS...)
1479 {
1480 	static if (FIELDS.length > 1) {
1481 		alias FilterSerializableFields = TypeTuple!(
1482 			FilterSerializableFields!(COMPOSITE, POLICY, FIELDS[0 .. $/2]),
1483 			FilterSerializableFields!(COMPOSITE, POLICY, FIELDS[$/2 .. $]));
1484 	} else static if (FIELDS.length == 1) {
1485 		alias T = COMPOSITE;
1486 		enum mname = FIELDS[0];
1487 		static if (isRWPlainField!(T, mname) || isRWField!(T, mname)) {
1488 			alias Tup = TypeTuple!(__traits(getMember, COMPOSITE, FIELDS[0]));
1489 			static if (Tup.length != 1) {
1490 				alias FilterSerializableFields = TypeTuple!(mname);
1491 			} else {
1492 				static if (!hasPolicyAttribute!(IgnoreAttribute, POLICY, __traits(getMember, T, mname)))
1493 				{
1494 					alias FilterSerializableFields = TypeTuple!(mname);
1495 				} else alias FilterSerializableFields = TypeTuple!();
1496 			}
1497 		} else alias FilterSerializableFields = TypeTuple!();
1498 	} else alias FilterSerializableFields = TypeTuple!();
1499 }
1500 
1501 private size_t getExpandedFieldCount(T, FIELDS...)()
1502 {
1503 	size_t ret = 0;
1504 	foreach (F; FIELDS) ret += TypeTuple!(__traits(getMember, T, F)).length;
1505 	return ret;
1506 }
1507 
1508 private template getExpandedFieldsData(T, FIELDS...)
1509 {
1510 	import std.meta : aliasSeqOf, staticMap;
1511 	import std.range : repeat, zip, iota;
1512 
1513 	enum subfieldsCount(alias F) = TypeTuple!(__traits(getMember, T, F)).length;
1514 	alias processSubfield(alias F) = aliasSeqOf!(zip(repeat(F), iota(subfieldsCount!F)));
1515 	alias getExpandedFieldsData = staticMap!(processSubfield, FIELDS);
1516 }
1517 
1518 /// Uses Base64 representation for `ubyte[]` instead of `to!string`
1519 public class Base64ArrayPolicy (R) if (isArray!R && is(ElementType!R : ubyte))
1520 {
1521 	public static string toRepresentation (in R data) @safe pure
1522 	{
1523 		import std.base64 : Base64;
1524 		return Base64.encode(data);
1525 	}
1526 
1527 	public static ubyte[] fromRepresentation (in string data) @safe pure
1528 	{
1529 		import std.base64 : Base64;
1530 		return Base64.decode(data);
1531 	}
1532 }
1533 
1534 /******************************************************************************/
1535 /* General serialization unit testing                                         */
1536 /******************************************************************************/
1537 
1538 version (unittest) {
1539 	static assert(isSafeSerializer!TestSerializer);
1540 	static assert(isSafeDeserializer!TestSerializer);
1541 
1542 	private struct TestSerializer {
1543 		import std.array, std.conv, std.range, std.string, std.typecons;
1544 
1545 		string result;
1546 
1547 		enum isSupportedValueType(T) = is(T == string) || is(T == typeof(null)) || is(T == float) || is (T == int);
1548 
1549 		template unqualSeq(Specs...)
1550 		{
1551 			static if (Specs.length == 0) alias unqualSeq = AliasSeq!();
1552 			else static if (is(Specs[0])) alias unqualSeq = AliasSeq!(Unqual!(Specs[0]), unqualSeq!(Specs[1 .. $]));
1553 			else alias unqualSeq = AliasSeq!(Specs[0], unqualSeq!(Specs[1 .. $]));
1554 		}
1555 
1556 		template unqualType(T) {
1557 			static if (isAssociativeArray!T) alias unqualType = Unqual!(ValueType!T)[Unqual!(KeyType!T)];
1558 			else static if (isTuple!T) alias unqualType = Tuple!(unqualSeq!(TemplateArgsOf!T));
1559 			else static if (isArray!T && !isSomeString!T) alias unqualType = Unqual!(ElementType!T)[];
1560 			else alias unqualType = Unqual!T;
1561 		}
1562 
1563 		string getSerializedResult() @safe { return result; }
1564 		void beginWriteDictionary(Traits)() { result ~= "D("~unqualType!(Traits.Type).mangleof~"){"; }
1565 		void endWriteDictionary(Traits)() { result ~= "}D("~unqualType!(Traits.Type).mangleof~")"; }
1566 		void beginWriteDictionaryEntry(Traits)(string name) { result ~= "DE("~unqualType!(Traits.Type).mangleof~","~name~")("; }
1567 		void endWriteDictionaryEntry(Traits)(string name) { result ~= ")DE("~unqualType!(Traits.Type).mangleof~","~name~")"; }
1568 		void beginWriteArray(Traits)(size_t length) { result ~= "A("~unqualType!(Traits.Type).mangleof~")["~length.to!string~"]["; }
1569 		void endWriteArray(Traits)() { result ~= "]A("~unqualType!(Traits.Type).mangleof~")"; }
1570 		void beginWriteArrayEntry(Traits)(size_t i) { result ~= "AE("~unqualType!(Traits.Type).mangleof~","~i.to!string~")("; }
1571 		void endWriteArrayEntry(Traits)(size_t i) { result ~= ")AE("~unqualType!(Traits.Type).mangleof~","~i.to!string~")"; }
1572 		void writeValue(Traits, T)(T value) {
1573 			if (is(T == typeof(null))) result ~= "null";
1574 			else {
1575 				assert(isSupportedValueType!(unqualType!T));
1576 				result ~= "V("~(unqualType!T).mangleof~")("~value.to!string~")";
1577 			}
1578 		}
1579 
1580 		// deserialization
1581 		void readDictionary(Traits)(scope void delegate(string) @safe entry_callback)
1582 		{
1583 			skip("D("~unqualType!(Traits.Type).mangleof~"){");
1584 			while (result.startsWith("DE(")) {
1585 				result = result[3 .. $];
1586 				auto idx = result.indexOf(',');
1587 				auto idx2 = result.indexOf(")(");
1588 				assert(idx > 0 && idx2 > idx);
1589 				auto t = result[0 .. idx];
1590 				auto n = result[idx+1 .. idx2];
1591 				result = result[idx2+2 .. $];
1592 				entry_callback(n);
1593 				skip(")DE("~t~","~n~")");
1594 			}
1595 			skip("}D("~unqualType!(Traits.Type).mangleof~")");
1596 		}
1597 
1598 		void beginReadDictionaryEntry(Traits)(string name) {}
1599 		void endReadDictionaryEntry(Traits)(string name) {}
1600 
1601 		void readArray(Traits)(scope void delegate(size_t) @safe size_callback, scope void delegate() @safe entry_callback)
1602 		{
1603 			skip("A("~unqualType!(Traits.Type).mangleof~")[");
1604 			auto bidx = result.indexOf("][");
1605 			assert(bidx > 0);
1606 			auto cnt = result[0 .. bidx].to!size_t;
1607 			result = result[bidx+2 .. $];
1608 
1609 			size_t i = 0;
1610 			while (result.startsWith("AE(")) {
1611 				result = result[3 .. $];
1612 				auto idx = result.indexOf(',');
1613 				auto idx2 = result.indexOf(")(");
1614 				assert(idx > 0 && idx2 > idx);
1615 				auto t = result[0 .. idx];
1616 				auto n = result[idx+1 .. idx2];
1617 				result = result[idx2+2 .. $];
1618 				assert(n == i.to!string);
1619 				entry_callback();
1620 				skip(")AE("~t~","~n~")");
1621 				i++;
1622 			}
1623 			skip("]A("~unqualType!(Traits.Type).mangleof~")");
1624 
1625 			assert(i == cnt);
1626 		}
1627 
1628 		void beginReadArrayEntry(Traits)(size_t index) {}
1629 		void endReadArrayEntry(Traits)(size_t index) {}
1630 
1631 		T readValue(Traits, T)()
1632 		{
1633 			skip("V("~unqualType!T.mangleof~")(");
1634 			auto idx = result.indexOf(')');
1635 			assert(idx >= 0);
1636 			auto ret = result[0 .. idx].to!T;
1637 			result = result[idx+1 .. $];
1638 			return ret;
1639 		}
1640 
1641 		void skip(string prefix)
1642 		@safe {
1643 			assert(result.startsWith(prefix), prefix ~ " vs. " ~ result);
1644 			result = result[prefix.length .. $];
1645 		}
1646 
1647 		bool tryReadNull(Traits)()
1648 		{
1649 			if (result.startsWith("null")) {
1650 				result = result[4 .. $];
1651 				return true;
1652 			} else return false;
1653 		}
1654 	}
1655 }
1656 
1657 unittest { // basic serialization behavior
1658 	import std.typecons : Nullable;
1659 
1660 	static void test(T)(auto ref T value, string expected) {
1661 		assert(serialize!TestSerializer(value) == expected, serialize!TestSerializer(value));
1662 		static if (isPointer!T) {
1663 			if (value) assert(*deserialize!(TestSerializer, T)(expected) == *value);
1664 			else assert(deserialize!(TestSerializer, T)(expected) is null);
1665 		} else static if (is(T == Nullable!U, U)) {
1666 			if (value.isNull()) assert(deserialize!(TestSerializer, T)(expected).isNull);
1667 			else assert(deserialize!(TestSerializer, T)(expected) == value);
1668 		} else assert(deserialize!(TestSerializer, T)(expected) == value);
1669 	}
1670 
1671 	test("hello", "V(Aya)(hello)");
1672 	test(12, "V(i)(12)");
1673 	test(12.0, "V(Aya)(12)");
1674 	test(12.0f, "V(f)(12)");
1675 	assert(serialize!TestSerializer(null) ==  "null");
1676 	test(["hello", "world"], "A(AAya)[2][AE(Aya,0)(V(Aya)(hello))AE(Aya,0)AE(Aya,1)(V(Aya)(world))AE(Aya,1)]A(AAya)");
1677 	string mangleOfAA = (string[string]).mangleof;
1678 	test(["hello": "world"], "D(" ~ mangleOfAA ~ "){DE(Aya,hello)(V(Aya)(world))DE(Aya,hello)}D(" ~ mangleOfAA ~ ")");
1679 	test(cast(int*)null, "null");
1680 	int i = 42;
1681 	test(&i, "V(i)(42)");
1682 	Nullable!int j;
1683 	test(j, "null");
1684 	j = 42;
1685 	test(j, "V(i)(42)");
1686 }
1687 
1688 unittest { // basic user defined types
1689 	static struct S { string f; }
1690 	enum Sm = S.mangleof;
1691 	auto s = S("hello");
1692 	enum s_ser = "D("~Sm~"){DE(Aya,f)(V(Aya)(hello))DE(Aya,f)}D("~Sm~")";
1693 	assert(serialize!TestSerializer(s) == s_ser, serialize!TestSerializer(s));
1694 	assert(deserialize!(TestSerializer, S)(s_ser) == s);
1695 
1696 	static class C { string f; }
1697 	enum Cm = C.mangleof;
1698 	C c;
1699 	assert(serialize!TestSerializer(c) == "null");
1700 	c = new C;
1701 	c.f = "hello";
1702 	enum c_ser = "D("~Cm~"){DE(Aya,f)(V(Aya)(hello))DE(Aya,f)}D("~Cm~")";
1703 	assert(serialize!TestSerializer(c) == c_ser);
1704 	assert(deserialize!(TestSerializer, C)(c_ser).f == c.f);
1705 
1706 	enum E { hello, world }
1707 	assert(serialize!TestSerializer(E.hello) == "V(i)(0)");
1708 	assert(serialize!TestSerializer(E.world) == "V(i)(1)");
1709 }
1710 
1711 unittest { // tuple serialization
1712 	import std.typecons : Tuple;
1713 
1714 	static struct S(T...) { T f; }
1715 	enum Sm = S!(int, string).mangleof;
1716 	enum Tum = Tuple!(int, string).mangleof;
1717 	const s = S!(int, string)(42, "hello");
1718 
1719 	const ss = serialize!TestSerializer(s);
1720 	const es = "D("~Sm~"){DE("~Tum~",f)(A("~Tum~")[2][AE(i,0)(V(i)(42))AE(i,0)AE(Aya,1)(V(Aya)(hello))AE(Aya,1)]A("~Tum~"))DE("~Tum~",f)}D("~Sm~")";
1721 	assert(ss == es);
1722 
1723 	const dss = deserialize!(TestSerializer, typeof(s))(ss);
1724 	assert(dss == s);
1725 
1726 	static struct T { @asArray S!(int, string) g; }
1727 	enum Tm = T.mangleof;
1728 	const t = T(s);
1729 
1730 	const st = serialize!TestSerializer(t);
1731 	const et = "D("~Tm~"){DE("~Sm~",g)(A("~Sm~")[2][AE(i,0)(V(i)(42))AE(i,0)AE(Aya,1)(V(Aya)(hello))AE(Aya,1)]A("~Sm~"))DE("~Sm~",g)}D("~Tm~")";
1732 	assert(st == et);
1733 
1734 	const dst = deserialize!(TestSerializer, typeof(t))(st);
1735 	assert(dst == t);
1736 }
1737 
1738 unittest { // named tuple serialization
1739 	import std.typecons : tuple;
1740 
1741 	static struct I {
1742 		int i;
1743 	}
1744 
1745 	static struct S {
1746 		int x;
1747 		string s_;
1748 	}
1749 
1750 	static struct T {
1751 		@asArray
1752 		typeof(tuple!(FieldNameTuple!I)(I.init.tupleof)) tuple1AsArray;
1753 
1754 		@name(fullyQualifiedName!I)
1755 		typeof(tuple!(FieldNameTuple!I)(I.init.tupleof)) tuple1AsDictionary;
1756 
1757 		@asArray
1758 		typeof(tuple!(FieldNameTuple!S)(S.init.tupleof)) tuple2AsArray;
1759 
1760 		@name(fullyQualifiedName!S)
1761 		typeof(tuple!(FieldNameTuple!S)(S.init.tupleof)) tuple2AsDictionary;
1762 	}
1763 
1764 	const i = I(42);
1765 	const s = S(42, "hello");
1766 	const T t = { i.tupleof, i.tupleof, s.tupleof, s.tupleof };
1767 
1768 	const st = serialize!TestSerializer(t);
1769 
1770 	enum Tm = T.mangleof;
1771 	enum TuIm = typeof(T.tuple1AsArray).mangleof;
1772 	enum TuSm = typeof(T.tuple2AsArray).mangleof;
1773 
1774 	const et =
1775 		"D("~Tm~")"~
1776 		"{"~
1777 			"DE("~TuIm~",tuple1AsArray)"~
1778 			"("~
1779 				"V(i)(42)"~
1780 			")"~
1781 			"DE("~TuIm~",tuple1AsArray)"~
1782 			"DE("~TuIm~","~fullyQualifiedName!I~")"~
1783 			"("~
1784 				"D("~TuIm~")"~
1785 				"{"~
1786 					"DE(i,i)"~
1787 					"("~
1788 						"V(i)(42)"~
1789 					")"~
1790 					"DE(i,i)"~
1791 				"}"~
1792 				"D("~TuIm~")"~
1793 			")"~
1794 			"DE("~TuIm~","~fullyQualifiedName!I~")"~
1795 			"DE("~TuSm~",tuple2AsArray)"~
1796 			"("~
1797 				"A("~TuSm~")[2]"~
1798 				"["~
1799 					"AE(i,0)"~
1800 					"("~
1801 						"V(i)(42)"~
1802 					")"~
1803 					"AE(i,0)"~
1804 					"AE(Aya,1)"~
1805 					"("~
1806 						"V(Aya)(hello)"~
1807 					")"~
1808 					"AE(Aya,1)"~
1809 				"]"~
1810 				"A("~TuSm~")"~
1811 			")"~
1812 			"DE("~TuSm~",tuple2AsArray)"~
1813 			"DE("~TuSm~","~fullyQualifiedName!S~")"~
1814 			"("~
1815 				"D("~TuSm~")"~
1816 				"{"~
1817 					"DE(i,x)"~
1818 					"("~
1819 						"V(i)(42)"~
1820 					")"~
1821 					"DE(i,x)"~
1822 					"DE(Aya,s)"~
1823 					"("~
1824 						"V(Aya)(hello)"~
1825 					")"~
1826 					"DE(Aya,s)"~
1827 				"}"~
1828 				"D("~TuSm~")"~
1829 			")"~
1830 			"DE("~TuSm~","~fullyQualifiedName!S~")"~
1831 		"}"~
1832 		"D("~Tm~")";
1833 	assert(st == et);
1834 
1835 	const dst = deserialize!(TestSerializer, typeof(t))(st);
1836 	assert(dst == t);
1837 }
1838 
1839 unittest { // testing the various UDAs
1840 	enum E { hello, world }
1841 	enum Em = E.mangleof;
1842 	static struct S {
1843 		@byName E e;
1844 		@ignore int i;
1845 		@optional float f;
1846 	}
1847 	enum Sm = S.mangleof;
1848 	auto s = S(E.world, 42, 1.0f);
1849 	assert(serialize!TestSerializer(s) ==
1850 		"D("~Sm~"){DE("~Em~",e)(V(Aya)(world))DE("~Em~",e)DE(f,f)(V(f)(1))DE(f,f)}D("~Sm~")");
1851 }
1852 
1853 unittest { // custom serialization support
1854 	// iso-ext
1855 	import std.datetime;
1856 	auto t = TimeOfDay(6, 31, 23);
1857 	assert(serialize!TestSerializer(t) == "V(Aya)(06:31:23)");
1858 	auto d = Date(1964, 1, 23);
1859 	assert(serialize!TestSerializer(d) == "V(Aya)(1964-01-23)");
1860 	auto dt = DateTime(d, t);
1861 	assert(serialize!TestSerializer(dt) == "V(Aya)(1964-01-23T06:31:23)");
1862 	auto st = SysTime(dt, UTC());
1863 	assert(serialize!TestSerializer(st) == "V(Aya)(1964-01-23T06:31:23Z)");
1864 }
1865 
1866 @safe unittest { // custom serialization support
1867 	// string
1868 	static struct S1 { int i; string toString() const @safe { return "hello"; } static S1 fromString(string) @safe { return S1.init; } }
1869 	static struct S2 { int i; string toString() const { return "hello"; } }
1870 	enum S2m = S2.mangleof;
1871 	static struct S3 { int i; static S3 fromString(string) { return S3.init; } }
1872 	enum S3m = S3.mangleof;
1873 	assert(serialize!TestSerializer(S1.init) == "V(Aya)(hello)");
1874 	assert(serialize!TestSerializer(S2.init) == "D("~S2m~"){DE(i,i)(V(i)(0))DE(i,i)}D("~S2m~")");
1875 	assert(serialize!TestSerializer(S3.init) == "D("~S3m~"){DE(i,i)(V(i)(0))DE(i,i)}D("~S3m~")");
1876 
1877 	// custom
1878 	static struct C1 { int i; float toRepresentation() const @safe { return 1.0f; } static C1 fromRepresentation(float f) @safe { return C1.init; } }
1879 	static struct C2 { int i; float toRepresentation() const { return 1.0f; } }
1880 	enum C2m = C2.mangleof;
1881 	static struct C3 { int i; static C3 fromRepresentation(float f) { return C3.init; } }
1882 	enum C3m = C3.mangleof;
1883 	assert(serialize!TestSerializer(C1.init) == "V(f)(1)");
1884 	assert(serialize!TestSerializer(C2.init) == "D("~C2m~"){DE(i,i)(V(i)(0))DE(i,i)}D("~C2m~")");
1885 	assert(serialize!TestSerializer(C3.init) == "D("~C3m~"){DE(i,i)(V(i)(0))DE(i,i)}D("~C3m~")");
1886 }
1887 
1888 unittest // Testing corner case: member function returning by ref
1889 {
1890 	import vibe.data.json;
1891 
1892 	static struct S
1893 	{
1894 		int i;
1895 		ref int foo() return { return i; }
1896 	}
1897 
1898 	static assert(__traits(compiles, { S().serializeToJson(); }));
1899 	static assert(__traits(compiles, { Json().deserializeJson!S(); }));
1900 
1901 	auto s = S(1);
1902 	assert(s.serializeToJson().deserializeJson!S() == s);
1903 }
1904 
1905 unittest // Testing corner case: Variadic template constructors and methods
1906 {
1907 	import vibe.data.json;
1908 
1909 	static struct S
1910 	{
1911 		int i;
1912 		this(Args...)(Args args) {}
1913 		int foo(Args...)(Args args) { return i; }
1914 		ref int bar(Args...)(Args args) { return i; }
1915 	}
1916 
1917 	static assert(__traits(compiles, { S().serializeToJson(); }));
1918 	static assert(__traits(compiles, { Json().deserializeJson!S(); }));
1919 
1920 	auto s = S(1);
1921 	assert(s.serializeToJson().deserializeJson!S() == s);
1922 }
1923 
1924 @safe unittest // Make sure serializing through properties still works
1925 {
1926 	import vibe.data.json;
1927 
1928 	static struct S
1929 	{
1930 		@safe:
1931 		public int i;
1932 		private int privateJ;
1933 
1934 		@property int j() @safe { return privateJ; }
1935 		@property void j(int j) @safe { privateJ = j; }
1936 	}
1937 
1938 	auto s = S(1, 2);
1939 	assert(s.serializeToJson().deserializeJson!S() == s);
1940 }
1941 
1942 @safe unittest // Immutable data deserialization
1943 {
1944 	import vibe.data.json;
1945 
1946 	static struct S {
1947 		int a;
1948 	}
1949 	static class C {
1950 		immutable(S)[] arr;
1951 	}
1952 
1953 	auto c = new C;
1954 	c.arr ~= S(10);
1955 	auto d = c.serializeToJson().deserializeJson!(immutable C);
1956 	static assert(is(typeof(d) == immutable C));
1957 	assert(d.arr == c.arr);
1958 }
1959 
1960 unittest { // test BitFlags serialization
1961 	import std.typecons : BitFlags;
1962 
1963 	enum Flag {
1964 		a = 1<<0,
1965 		b = 1<<1,
1966 		c = 1<<2
1967 	}
1968 	enum Flagm = Flag.mangleof;
1969 
1970 	alias Flags = BitFlags!Flag;
1971 	enum Flagsm = Flags.mangleof;
1972 
1973 	enum Fi_ser = "A("~Flagsm~")[0][]A("~Flagsm~")";
1974 	assert(serialize!TestSerializer(Flags.init) == Fi_ser);
1975 
1976 	enum Fac_ser = "A("~Flagsm~")[2][AE("~Flagm~",0)(V(i)(1))AE("~Flagm~",0)AE("~Flagm~",1)(V(i)(4))AE("~Flagm~",1)]A("~Flagsm~")";
1977 	assert(serialize!TestSerializer(Flags(Flag.a, Flag.c)) == Fac_ser);
1978 
1979 	struct S { @byName Flags f; }
1980 	enum Sm = S.mangleof;
1981 	enum Sac_ser = "D("~Sm~"){DE("~Flagsm~",f)(A("~Flagsm~")[2][AE("~Flagm~",0)(V(Aya)(a))AE("~Flagm~",0)AE("~Flagm~",1)(V(Aya)(c))AE("~Flagm~",1)]A("~Flagsm~"))DE("~Flagsm~",f)}D("~Sm~")";
1982 
1983 	assert(serialize!TestSerializer(S(Flags(Flag.a, Flag.c))) == Sac_ser);
1984 
1985 	assert(deserialize!(TestSerializer, Flags)(Fi_ser) == Flags.init);
1986 	assert(deserialize!(TestSerializer, Flags)(Fac_ser) == Flags(Flag.a, Flag.c));
1987 	assert(deserialize!(TestSerializer, S)(Sac_ser) == S(Flags(Flag.a, Flag.c)));
1988 }
1989 
1990 @safe unittest { // issue #1182
1991 	struct T {
1992 		int x;
1993 		string y;
1994 	}
1995 	struct S {
1996 		@asArray T t;
1997 	}
1998 
1999 	auto s = S(T(42, "foo"));
2000 	enum Sm = S.mangleof;
2001 	enum Tm = T.mangleof;
2002 	enum s_ser = "D("~Sm~"){DE("~Tm~",t)(A("~Tm~")[2][AE(i,0)(V(i)(42))AE(i,0)AE(Aya,1)(V(Aya)(foo))AE(Aya,1)]A("~Tm~"))DE("~Tm~",t)}D("~Sm~")";
2003 
2004 	auto serialized = serialize!TestSerializer(s);
2005 	assert(serialized == s_ser, serialized);
2006 	assert(deserialize!(TestSerializer, S)(serialized) == s);
2007 }
2008 
2009 @safe unittest { // issue #1352 - ingore per policy
2010 	struct P1 {}
2011 	struct P2 {}
2012 
2013 	struct T {
2014 		@ignore int a = 5;
2015 		@ignore!P1 @ignore!P2 int b = 6;
2016 		@ignore!P1 c = 7;
2017 		int d = 8;
2018 	}
2019 
2020 	auto t = T(1, 2, 3, 4);
2021 	auto Tm = T.mangleof;
2022 	auto t_ser_plain = "D("~Tm~"){DE(i,b)(V(i)(2))DE(i,b)DE(i,c)(V(i)(3))DE(i,c)DE(i,d)(V(i)(4))DE(i,d)}D("~Tm~")";
2023 	auto t_ser_p1 = "D("~Tm~"){DE(i,d)(V(i)(4))DE(i,d)}D("~Tm~")";
2024 	auto t_ser_p2 = "D("~Tm~"){DE(i,c)(V(i)(3))DE(i,c)DE(i,d)(V(i)(4))DE(i,d)}D("~Tm~")";
2025 
2026 	{
2027 		auto serialized_plain = serialize!TestSerializer(t);
2028 		assert(serialized_plain == t_ser_plain);
2029 		assert(deserialize!(TestSerializer, T)(serialized_plain) == T(5, 2, 3, 4));
2030 	}
2031 
2032 	{
2033 		auto serialized_p1 = serializeWithPolicy!(TestSerializer, P1)(t);
2034 		assert(serialized_p1 == t_ser_p1, serialized_p1);
2035 		assert(deserializeWithPolicy!(TestSerializer, P1, T)(serialized_p1) == T(5, 6, 7, 4));
2036 	}
2037 
2038 	{
2039 		auto serialized_p2 = serializeWithPolicy!(TestSerializer, P2)(t);
2040 		assert(serialized_p2 == t_ser_p2);
2041 		assert(deserializeWithPolicy!(TestSerializer, P2, T)(serialized_p2) == T(5, 6, 3, 4));
2042 	}
2043 }
2044 
2045 unittest {
2046 	import std.conv : to;
2047 	import std.string : toLower, toUpper;
2048 
2049 	template P(T) if (is(T == enum)) {
2050 		@safe:
2051 		static string toRepresentation(T v) { return v.to!string.toLower(); }
2052 		static T fromRepresentation(string str) { return str.toUpper().to!T; }
2053 	}
2054 
2055 
2056 	enum E {
2057 		RED,
2058 		GREEN
2059 	}
2060 
2061 	assert(P!E.fromRepresentation("green") == E.GREEN);
2062 	static assert(isPolicySerializable!(P, E));
2063 
2064 	auto ser_red = "V(Aya)(red)";
2065 	assert(serializeWithPolicy!(TestSerializer, P)(E.RED) == ser_red, serializeWithPolicy!(TestSerializer, P)(E.RED));
2066 	assert(deserializeWithPolicy!(TestSerializer, P, E)(ser_red) == E.RED);
2067 
2068 	import vibe.data.json : Json, JsonSerializer;
2069 	assert(serializeWithPolicy!(JsonSerializer, P)(E.RED) == Json("red"));
2070 }
2071 
2072 unittest {
2073 	static struct R { int y; }
2074 	static struct Custom {
2075 		@safe:
2076 		int x;
2077 		R toRepresentation() const { return R(x); }
2078 		static Custom fromRepresentation(R r) { return Custom(r.y); }
2079 	}
2080 
2081 	auto c = Custom(42);
2082 	auto Rn = R.mangleof;
2083 	auto ser = serialize!TestSerializer(c);
2084 	assert(ser == "D("~Rn~"){DE(i,y)(V(i)(42))DE(i,y)}D("~Rn~")");
2085 	auto deser = deserialize!(TestSerializer, Custom)(ser);
2086 	assert(deser.x == 42);
2087 }
2088 
2089 unittest {
2090 	import std.typecons : Typedef;
2091 	alias T = Typedef!int;
2092 	auto ser = serialize!TestSerializer(T(42));
2093 	assert(ser == "V(i)(42)", ser);
2094 	auto deser = deserialize!(TestSerializer, T)(ser);
2095 	assert(deser == 42);
2096 }
2097 
2098 @safe unittest {
2099 	static struct Foo { Foo[] foos; }
2100 	Foo f;
2101 	string ser = serialize!TestSerializer(f);
2102 	assert(deserialize!(TestSerializer, Foo)(ser) == f);
2103 }
2104 
2105 @system unittest {
2106 	static struct SystemSerializer {
2107 		TestSerializer ser;
2108 		alias ser this;
2109 		this(string s) { ser.result = s; }
2110 		T readValue(Traits, T)() @system { return ser.readValue!(Traits, T); }
2111 		void writeValue(Traits, T)(T value) @system { ser.writeValue!(Traits, T)(value); }
2112 		void readDictionary(Traits)(scope void delegate(string) @system entry_callback) { return ser.readDictionary!Traits((s) @trusted { entry_callback(s); }); }
2113 		void readArray(Traits)(scope void delegate(size_t) @system size_callback, scope void delegate() @system entry_callback) { ser.readArray!Traits((s) @trusted { size_callback(s); }, () @trusted { entry_callback(); }); }
2114 	}
2115 
2116 	static struct Bar { Bar[] foos; int i; }
2117 	Bar f;
2118 	string ser = serialize!SystemSerializer(f);
2119 	assert(deserialize!(SystemSerializer, Bar)(ser) == f);
2120 }
2121 
2122 @safe unittest {
2123 	static struct S { @name("+foo") int bar; }
2124 	auto Sn = S.mangleof;
2125 	auto s = S(42);
2126 	string ser = serialize!TestSerializer(s);
2127 	assert(ser == "D("~Sn~"){DE(i,+foo)(V(i)(42))DE(i,+foo)}D("~Sn~")", ser);
2128 	auto deser = deserialize!(TestSerializer, S)(ser);
2129 	assert(deser.bar == 42);
2130 }
2131 
2132 @safe unittest {
2133 	static struct S { int bar_; }
2134 	auto Sn = S.mangleof;
2135 	auto s = S(42);
2136 	string ser = serialize!TestSerializer(s);
2137 	assert(ser == "D("~Sn~"){DE(i,bar)(V(i)(42))DE(i,bar)}D("~Sn~")", ser);
2138 	auto deser = deserialize!(TestSerializer, S)(ser);
2139 	assert(deser.bar_ == 42);
2140 }
2141 
2142 @safe unittest { // issue 1941
2143 	static struct Bar { Bar[] foos; int i; }
2144 	Bar b1 = {[{null, 2}], 1};
2145 	auto s = serialize!TestSerializer(b1);
2146 	auto b = deserialize!(TestSerializer, Bar)(s);
2147 	assert(b.i == 1);
2148 	assert(b.foos.length == 1);
2149 	assert(b.foos[0].i == 2);
2150 }
2151 
2152 unittest { // issue 1991 - @system property getters/setters does not compile
2153 	static class A {
2154 		@safe:
2155 		@property @name("foo") {
2156 			string fooString() const { return "a"; }
2157 			void fooString(string a) {  }
2158 		}
2159 	}
2160 
2161 	auto a1 = new A;
2162 	auto b = serialize!TestSerializer(a1);
2163 	auto a2 = deserialize!(TestSerializer, A)(b);
2164 }
2165 
2166 unittest { // issue #2110 - single-element tuples
2167 	static struct F { int field; }
2168 
2169 	{
2170 		static struct S { typeof(F.init.tupleof) fields; }
2171 		auto b = serialize!TestSerializer(S(42));
2172 		auto a = deserialize!(TestSerializer, S)(b);
2173 		assert(a.fields[0] == 42);
2174 	}
2175 
2176 	{
2177 		static struct T { @asArray typeof(F.init.tupleof) fields; }
2178 		auto b = serialize!TestSerializer(T(42));
2179 		auto a = deserialize!(TestSerializer, T)(b);
2180 		assert(a.fields[0] == 42);
2181 	}
2182 }
2183 
2184 @safe unittest {
2185 	import std.typecons : Nullable;
2186 
2187 	struct S {
2188 		@embedNullable Nullable!int x;
2189 		@embedNullable Nullable!string s;
2190 	}
2191 
2192 	enum Sn = S.mangleof;
2193 
2194 	auto s = S(Nullable!int(3), Nullable!string.init);
2195 	auto expected = "D("~Sn~"){DE(i,x)(V(i)(3))DE(i,x)}D("~Sn~")";
2196 
2197 	assert(serialize!TestSerializer(s) == expected, serialize!TestSerializer(s));
2198 	assert(deserialize!(TestSerializer, S)(expected) == s);
2199 
2200 	s.s = "hello";
2201 	expected = "D("~Sn~"){DE(i,x)(V(i)(3))DE(i,x)DE(Aya,s)(V(Aya)(hello))DE(Aya,s)}D("~Sn~")";
2202 	assert(serialize!TestSerializer(s) == expected, serialize!TestSerializer(s));
2203 	assert(deserialize!(TestSerializer, S)(expected) == s);
2204 
2205 	s.x.nullify();
2206 	expected = "D("~Sn~"){DE(Aya,s)(V(Aya)(hello))DE(Aya,s)}D("~Sn~")";
2207 	assert(serialize!TestSerializer(s) == expected);
2208 	assert(deserialize!(TestSerializer, S)(expected) == s);
2209 
2210 	s.s.nullify();
2211 	expected = "D("~Sn~"){}D("~Sn~")";
2212 	assert(serialize!TestSerializer(s) == expected);
2213 	assert(deserialize!(TestSerializer, S)(expected) == s);
2214 }