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