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 `BitFlags!T` value will be serialized as `T[]`)
27 			$(LI Types satisfying the `isPolicySerializable` trait for the
28 				supplied `Policy` will be serialized as the value returned
29 				by the policy `toRepresentation` function (again subject to
30 				these rules).)
31 			$(LI Types satisfying the `isCustomSerializable` trait will be
32 				serialized as the value returned by their `toRepresentation`
33 				method (again subject to these rules).)
34 			$(LI Types satisfying the `isISOExtStringSerializable` trait will be
35 				serialized as a string, as returned by their `toISOExtString`
36 				method. This causes types such as `SysTime` to be serialized
37 				as strings.)
38 			$(LI Types satisfying the `isStringSerializable` trait will be
39 				serialized as a string, as returned by their `toString`
40 				method.)
41 			$(LI Struct and class types by default will be serialized as
42 				associative arrays, where the key is the name of the
43 				corresponding field (can be overridden using the `@name`
44 				attribute). If the struct/class is annotated with `@asArray`,
45 				it will instead be serialized as a flat array of values in the
46 				order of declaration. Null class references will be serialized
47 				as `null`.)
48 			$(LI Pointer types will be serialized as either `null`, or as
49 				the value they point to.)
50 			$(LI Built-in integers and floating point values, as well as
51 				boolean values will be converted to strings, if the serializer
52 				doesn't support them directly.)
53 		)
54 
55 		Note that no aliasing detection is performed, so that pointers, class
56 		references and arrays referencing the same memory will be serialized
57 		as multiple copies. When in turn deserializing the data, they will also
58 		end up as separate copies in memory.
59 
60 	Serializer_implementation:
61 		Serializers are implemented in terms of a struct with template methods that
62 		get called by the serialization framework:
63 
64 		---
65 		struct ExampleSerializer {
66 			enum isSupportedValueType(T) = is(T == string) || is(T == typeof(null));
67 
68 			// serialization
69 			auto getSerializedResult();
70 			void beginWriteDictionary(T)();
71 			void endWriteDictionary(T)();
72 			void beginWriteDictionaryEntry(T)(string name);
73 			void endWriteDictionaryEntry(T)(string name);
74 			void beginWriteArray(T)(size_t length);
75 			void endWriteArray(T)();
76 			void beginWriteArrayEntry(T)(size_t index);
77 			void endWriteArrayEntry(T)(size_t index);
78 			void writeValue(T)(T value);
79 
80 			// deserialization
81 			void readDictionary(T)(scope void delegate(string) entry_callback);
82 			void readArray(T)(scope void delegate(size_t) size_callback, scope void delegate() entry_callback);
83 			T readValue(T)();
84 			bool tryReadNull();
85 		}
86 		---
87 
88 	Copyright: © 2013-2014 rejectedsoftware e.K.
89 	License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file.
90 	Authors: Sönke Ludwig
91 */
92 module dub.internal.vibecompat.data.serialization;
93 
94 version (Have_vibe_d) public import vibe.data.serialization;
95 else:
96 
97 import dub.internal.vibecompat.data.utils;
98 
99 import std.array : Appender, appender;
100 import std.conv : to;
101 import std.exception : enforce;
102 import std.traits;
103 import std.typetuple;
104 
105 
106 /**
107 	Serializes a value with the given serializer.
108 
109 	The serializer must have a value result for the first form
110 	to work. Otherwise, use the range based form.
111 
112 	See_Also: `vibe.data.json.JsonSerializer`, `vibe.data.json.JsonStringSerializer`, `vibe.data.bson.BsonSerializer`
113 */
114 auto serialize(Serializer, T, ARGS...)(T value, ARGS args)
115 {
116 	auto serializer = Serializer(args);
117 	serialize(serializer, value);
118 	return serializer.getSerializedResult();
119 }
120 /// ditto
121 void serialize(Serializer, T)(ref Serializer serializer, T value)
122 {
123 	serializeImpl!(Serializer, DefaultPolicy, T)(serializer, value);
124 }
125 
126 /** Note that there is a convenience function `vibe.data.json.serializeToJson`
127 	that can be used instead of manually invoking `serialize`.
128 */
129 unittest {
130 	import dub.internal.vibecompat.data.json;
131 
132 	struct Test {
133 		int value;
134 		string text;
135 	}
136 
137 	Test test;
138 	test.value = 12;
139 	test.text = "Hello";
140 
141 	Json serialized = serialize!JsonSerializer(test);
142 	assert(serialized.value.get!int == 12);
143 	assert(serialized.text.get!string == "Hello");
144 }
145 
146 unittest {
147 	import dub.internal.vibecompat.data.json;
148 
149 	// Make sure that immutable(char[]) works just like string
150 	// (i.e., immutable(char)[]).
151 	immutable key = "answer";
152 	auto ints = [key: 42];
153 	auto serialized = serialize!JsonSerializer(ints);
154 	assert(serialized[key].get!int == 42);
155 }
156 
157 /**
158 	Serializes a value with the given serializer, representing values according to `Policy` when possible.
159 
160 	The serializer must have a value result for the first form
161 	to work. Otherwise, use the range based form.
162 
163 	See_Also: `vibe.data.json.JsonSerializer`, `vibe.data.json.JsonStringSerializer`, `vibe.data.bson.BsonSerializer`
164 */
165 auto serializeWithPolicy(Serializer, alias Policy, T, ARGS...)(T value, ARGS args)
166 {
167 	auto serializer = Serializer(args);
168 	serializeWithPolicy!(Serializer, Policy)(serializer, value);
169 	return serializer.getSerializedResult();
170 }
171 /// ditto
172 void serializeWithPolicy(Serializer, alias Policy, T)(ref Serializer serializer, T value)
173 {
174 	serializeImpl!(Serializer, Policy, T)(serializer, value);
175 }
176 ///
177 version (unittest)
178 {
179 	template SizePol(T)
180 	{
181 		import std.conv;
182 		import std.array;
183 
184 		string toRepresentation(T value) {
185 			return to!string(value.x) ~ "x" ~ to!string(value.y);
186 		}
187 		
188 		T fromRepresentation(string value) {
189 			string[] fields = value.split('x');
190 			alias fieldT = typeof(T.x);
191 			auto x = to!fieldT(fields[0]);
192 			auto y = to!fieldT(fields[1]);
193 			return T(x, y);
194 		}
195 	}
196 }
197 
198 ///
199 unittest {
200 	import dub.internal.vibecompat.data.json;
201 
202 	static struct SizeI {
203 		int x;
204 		int y;
205 	}
206 	SizeI sizeI = SizeI(1,2);
207 	Json serializedI = serializeWithPolicy!(JsonSerializer, SizePol)(sizeI);
208 	assert(serializedI.get!string == "1x2");
209 
210 	static struct SizeF {
211 		float x;
212 		float y;
213 	}
214 	SizeF sizeF = SizeF(0.1f,0.2f);
215 	Json serializedF = serializeWithPolicy!(JsonSerializer, SizePol)(sizeF);
216 	assert(serializedF.get!string == "0.1x0.2");
217 }
218 
219 
220 /**
221 	Deserializes and returns a serialized value.
222 
223 	serialized_data can be either an input range or a value containing
224 	the serialized data, depending on the type of serializer used.
225 
226 	See_Also: `vibe.data.json.JsonSerializer`, `vibe.data.json.JsonStringSerializer`, `vibe.data.bson.BsonSerializer`
227 */
228 T deserialize(Serializer, T, ARGS...)(ARGS args)
229 {
230 	auto deserializer = Serializer(args);
231 	return deserializeImpl!(T, DefaultPolicy, Serializer)(deserializer);
232 }
233 
234 /** Note that there is a convenience function `vibe.data.json.deserializeJson`
235 	that can be used instead of manually invoking `deserialize`.
236 */
237 unittest {
238 	import dub.internal.vibecompat.data.json;
239 
240 	struct Test {
241 		int value;
242 		string text;
243 	}
244 
245 	Json serialized = Json.emptyObject;
246 	serialized.value = 12;
247 	serialized.text = "Hello";
248 
249 	Test test = deserialize!(JsonSerializer, Test)(serialized);
250 	assert(test.value == 12);
251 	assert(test.text == "Hello");
252 }
253 
254 /**
255 	Deserializes and returns a serialized value, interpreting values according to `Policy` when possible.
256 
257 	serialized_data can be either an input range or a value containing
258 	the serialized data, depending on the type of serializer used.
259 
260 	See_Also: `vibe.data.json.JsonSerializer`, `vibe.data.json.JsonStringSerializer`, `vibe.data.bson.BsonSerializer`
261 */
262 T deserializeWithPolicy(Serializer, alias Policy, T, ARGS...)(ARGS args)
263 {
264 	auto deserializer = Serializer(args);
265 	return deserializeImpl!(T, Policy, Serializer)(deserializer);
266 }
267 
268 ///
269 unittest {
270 	import dub.internal.vibecompat.data.json;
271 	
272 	static struct SizeI {
273 		int x;
274 		int y;
275 	}
276 
277 	Json serializedI = "1x2";
278 	SizeI sizeI = deserializeWithPolicy!(JsonSerializer, SizePol, SizeI)(serializedI);
279 	assert(sizeI.x == 1);
280 	assert(sizeI.y == 2);
281 	
282 	static struct SizeF {
283 		float x;
284 		float y;
285 	}
286 	Json serializedF = "0.1x0.2";
287 	SizeF sizeF = deserializeWithPolicy!(JsonSerializer, SizePol, SizeF)(serializedF);
288 	assert(sizeF.x == 0.1f);
289 	assert(sizeF.y == 0.2f);
290 }
291 
292 private void serializeImpl(Serializer, alias Policy, T, ATTRIBUTES...)(ref Serializer serializer, T value)
293 {
294 	import std.typecons : BitFlags, Nullable, Tuple, tuple;
295 
296 	static assert(Serializer.isSupportedValueType!string, "All serializers must support string values.");
297 	static assert(Serializer.isSupportedValueType!(typeof(null)), "All serializers must support null values.");
298 
299 	alias TU = Unqual!T;
300 
301 	static if (is(TU == enum)) {
302 		static if (hasAttributeL!(ByNameAttribute, ATTRIBUTES)) {
303 			serializeImpl!(Serializer, Policy, string)(serializer, value.to!string());
304 		} else {
305 			serializeImpl!(Serializer, Policy, OriginalType!TU)(serializer, cast(OriginalType!TU)value);
306 		}
307 	} else static if (Serializer.isSupportedValueType!TU) {
308 		static if (is(TU == typeof(null))) serializer.writeValue!TU(null);
309 		else serializer.writeValue!TU(value);
310 	} else static if (/*isInstanceOf!(Tuple, TU)*/is(T == Tuple!TPS, TPS...)) {
311 		static if (TU.Types.length == 1) {
312 			serializeImpl!(Serializer, Policy, typeof(value[0]), ATTRIBUTES)(serializer, value[0]);
313 		} else {
314 			serializer.beginWriteArray!TU(value.length);
315 			foreach (i, TV; T.Types) {
316 				serializer.beginWriteArrayEntry!TV(i);
317 				serializeImpl!(Serializer, Policy, TV, ATTRIBUTES)(serializer, value[i]);
318 				serializer.endWriteArrayEntry!TV(i);
319 			}
320 			serializer.endWriteArray!TU();
321 		}
322 	} else static if (isArray!TU) {
323 		alias TV = typeof(value[0]);
324 		serializer.beginWriteArray!TU(value.length);
325 		foreach (i, ref el; value) {
326 			serializer.beginWriteArrayEntry!TV(i);
327 			serializeImpl!(Serializer, Policy, TV, ATTRIBUTES)(serializer, el);
328 			serializer.endWriteArrayEntry!TV(i);
329 		}
330 		serializer.endWriteArray!TU();
331 	} else static if (isAssociativeArray!TU) {
332 		alias TK = KeyType!TU;
333 		alias TV = ValueType!TU;
334 		static if (__traits(compiles, serializer.beginWriteDictionary!TU(0))) {
335 			auto nfields = value.length;
336 			serializer.beginWriteDictionary!TU(nfields);
337 		} else {
338 			serializer.beginWriteDictionary!TU();
339 		}
340 		foreach (key, ref el; value) {
341 			string keyname;
342 			static if (is(TK : string)) keyname = key;
343 			else static if (is(TK : real) || is(TK : long) || is(TK == enum)) keyname = key.to!string;
344 			else static if (isStringSerializable!TK) keyname = key.toString();
345 			else static assert(false, "Associative array keys must be strings, numbers, enums, or have toString/fromString methods.");
346 			serializer.beginWriteDictionaryEntry!TV(keyname);
347 			serializeImpl!(Serializer, Policy, TV, ATTRIBUTES)(serializer, el);
348 			serializer.endWriteDictionaryEntry!TV(keyname);
349 		}
350 		static if (__traits(compiles, serializer.endWriteDictionary!TU(0))) {
351 			serializer.endWriteDictionary!TU(nfields);
352 		} else {
353 			serializer.endWriteDictionary!TU();
354 		}
355 	} else static if (/*isInstanceOf!(Nullable, TU)*/is(T == Nullable!TPS, TPS...)) {
356 		if (value.isNull()) serializeImpl!(Serializer, Policy, typeof(null))(serializer, null);
357 		else serializeImpl!(Serializer, Policy, typeof(value.get()), ATTRIBUTES)(serializer, value.get());
358 	} else static if (is(T == BitFlags!E, E)) {
359 		size_t cnt = 0;
360 		foreach (v; EnumMembers!E)
361 			if (value & v)
362 				cnt++;
363 
364 		serializer.beginWriteArray!(E[])(cnt);
365 		cnt = 0;
366 		foreach (v; EnumMembers!E)
367 			if (value & v) {
368 				serializer.beginWriteArrayEntry!E(cnt);
369 				serializeImpl!(Serializer, Policy, E, ATTRIBUTES)(serializer, v);
370 				serializer.endWriteArrayEntry!E(cnt);
371 				cnt++;
372 			}
373 		serializer.endWriteArray!(E[])();
374 	} else static if (isPolicySerializable!(Policy, TU)) {
375 		alias CustomType = typeof(Policy!TU.toRepresentation(TU.init));
376 		serializeImpl!(Serializer, Policy, CustomType, ATTRIBUTES)(serializer, Policy!TU.toRepresentation(value));
377 	} else static if (isCustomSerializable!TU) {
378 		alias CustomType = typeof(T.init.toRepresentation());
379 		serializeImpl!(Serializer, Policy, CustomType, ATTRIBUTES)(serializer, value.toRepresentation());
380 	} else static if (isISOExtStringSerializable!TU) {
381 		serializer.writeValue(value.toISOExtString());
382 	} else static if (isStringSerializable!TU) {
383 		serializer.writeValue(value.toString());
384 	} else static if (is(TU == struct) || is(TU == class)) {
385 		static if (!hasSerializableFields!TU)
386 			pragma(msg, "Serializing composite type "~T.stringof~" which has no serializable fields");
387 		static if (is(TU == class)) {
388 			if (value is null) {
389 				serializeImpl!(Serializer, Policy, typeof(null))(serializer, null);
390 				return;
391 			}
392 		}
393 		static if (hasAttributeL!(AsArrayAttribute, ATTRIBUTES)) {
394 			enum nfields = getExpandedFieldCount!(TU, SerializableFields!TU);
395 			serializer.beginWriteArray!TU(nfields);
396 			foreach (mname; SerializableFields!TU) {
397 				alias TMS = TypeTuple!(typeof(__traits(getMember, value, mname)));
398 				foreach (j, TM; TMS) {
399 					alias TA = TypeTuple!(__traits(getAttributes, TypeTuple!(__traits(getMember, T, mname))[j]));
400 					serializer.beginWriteArrayEntry!TM(j);
401 					serializeImpl!(Serializer, Policy, TM, TA)(serializer, tuple(__traits(getMember, value, mname))[j]);
402 					serializer.endWriteArrayEntry!TM(j);
403 				}
404 			}
405 			serializer.endWriteArray!TU();
406 		} else {
407 			static if (__traits(compiles, serializer.beginWriteDictionary!TU(0))) {
408 				enum nfields = getExpandedFieldCount!(TU, SerializableFields!TU);
409 				serializer.beginWriteDictionary!TU(nfields);
410 			} else {
411 				serializer.beginWriteDictionary!TU();
412 			}
413 			foreach (mname; SerializableFields!TU) {
414 				alias TM = TypeTuple!(typeof(__traits(getMember, value, mname)));
415 				static if (TM.length == 1) {
416 					alias TA = TypeTuple!(__traits(getAttributes, __traits(getMember, T, mname)));
417 					enum name = getAttribute!(TU, mname, NameAttribute)(NameAttribute(underscoreStrip(mname))).name;
418 					auto vt = __traits(getMember, value, mname);
419 					serializer.beginWriteDictionaryEntry!(typeof(vt))(name);
420 					serializeImpl!(Serializer, Policy, typeof(vt), TA)(serializer, vt);
421 					serializer.endWriteDictionaryEntry!(typeof(vt))(name);
422 				} else {
423 					alias TA = TypeTuple!(); // FIXME: support attributes for tuples somehow
424 					enum name = underscoreStrip(mname);
425 					auto vt = tuple(__traits(getMember, value, mname));
426 					serializer.beginWriteDictionaryEntry!(typeof(vt))(name);
427 					serializeImpl!(Serializer, Policy, typeof(vt), TA)(serializer, vt);
428 					serializer.endWriteDictionaryEntry!(typeof(vt))(name);
429 				}
430 			}
431 			static if (__traits(compiles, serializer.endWriteDictionary!TU(0))) {
432 				serializer.endWriteDictionary!TU(nfields);
433 			} else {
434 				serializer.endWriteDictionary!TU();
435 			}
436 		}
437 	} else static if (isPointer!TU) {
438 		if (value is null) {
439 			serializer.writeValue(null);
440 			return;
441 		}
442 		serializeImpl!(Serializer, Policy, PointerTarget!TU)(serializer, *value);
443 	} else static if (is(TU == bool) || is(TU : real) || is(TU : long)) {
444 		serializeImpl!(Serializer, Policy, string)(serializer, to!string(value));
445 	} else static assert(false, "Unsupported serialization type: " ~ T.stringof);
446 }
447 
448 
449 private T deserializeImpl(T, alias Policy, Serializer, ATTRIBUTES...)(ref Serializer deserializer)
450 {
451 	import std.typecons : BitFlags, Nullable;
452 
453 	static assert(Serializer.isSupportedValueType!string, "All serializers must support string values.");
454 	static assert(Serializer.isSupportedValueType!(typeof(null)), "All serializers must support null values.");
455 
456 	static if (is(T == enum)) {
457 		static if (hasAttributeL!(ByNameAttribute, ATTRIBUTES)) {
458 			return deserializeImpl!(string, Policy, Serializer)(deserializer).to!T();
459 		} else {
460 			return cast(T)deserializeImpl!(OriginalType!T, Policy, Serializer)(deserializer);
461 		}
462 	} else static if (Serializer.isSupportedValueType!T) {
463 		return deserializer.readValue!T();
464 	} else static if (isStaticArray!T) {
465 		alias TV = typeof(T.init[0]);
466 		T ret;
467 		size_t i = 0;
468 		deserializer.readArray!T((sz) { assert(sz == 0 || sz == T.length); }, {
469 			assert(i < T.length);
470 			ret[i++] = deserializeImpl!(TV, Policy, Serializer, ATTRIBUTES)(deserializer);
471 		});
472 		return ret;
473 	} else static if (isDynamicArray!T) {
474 		alias TV = typeof(T.init[0]);
475 		//auto ret = appender!T();
476 		T ret; // Cannot use appender because of DMD BUG 10690/10859/11357
477 		deserializer.readArray!T((sz) { ret.reserve(sz); }, () {
478 			ret ~= deserializeImpl!(TV, Policy, Serializer, ATTRIBUTES)(deserializer);
479 		});
480 		return ret;//cast(T)ret.data;
481 	} else static if (isAssociativeArray!T) {
482 		alias TK = KeyType!T;
483 		alias TV = ValueType!T;
484 		T ret;
485 		deserializer.readDictionary!T((name) {
486 			TK key;
487 			static if (is(TK == string)) key = name;
488 			else static if (is(TK : real) || is(TK : long) || is(TK == enum)) key = name.to!TK;
489 			else static if (isStringSerializable!TK) key = TK.fromString(name);
490 			else static assert(false, "Associative array keys must be strings, numbers, enums, or have toString/fromString methods.");
491 			ret[key] = deserializeImpl!(TV, Policy, Serializer, ATTRIBUTES)(deserializer);
492 		});
493 		return ret;
494 	} else static if (isInstanceOf!(Nullable, T)) {
495 		if (deserializer.tryReadNull()) return T.init;
496 		return T(deserializeImpl!(typeof(T.init.get()), Policy, Serializer, ATTRIBUTES)(deserializer));
497 	} else static if (is(T == BitFlags!E, E)) {
498 		T ret;
499 		deserializer.readArray!(E[])((sz) {}, {
500 			ret |= deserializeImpl!(E, Policy, Serializer, ATTRIBUTES)(deserializer);
501 		});
502 		return ret;
503 	} else static if (isPolicySerializable!(Policy, T)) {
504 		alias CustomType = typeof(Policy!T.toRepresentation(T.init));
505 		return Policy!T.fromRepresentation(deserializeImpl!(CustomType, Policy, Serializer, ATTRIBUTES)(deserializer));
506 	} else static if (isCustomSerializable!T) {
507 		alias CustomType = typeof(T.init.toRepresentation());
508 		return T.fromRepresentation(deserializeImpl!(CustomType, Policy, Serializer, ATTRIBUTES)(deserializer));
509 	} else static if (isISOExtStringSerializable!T) {
510 		return T.fromISOExtString(deserializer.readValue!string());
511 	} else static if (isStringSerializable!T) {
512 		return T.fromString(deserializer.readValue!string());
513 	} else static if (is(T == struct) || is(T == class)) {
514 		static if (is(T == class)) {
515 			if (deserializer.tryReadNull()) return null;
516 		}
517 
518 		bool[__traits(allMembers, T).length] set;
519 		string name;
520 		T ret;
521 		static if (is(T == class)) ret = new T;
522 
523 		static if (hasAttributeL!(AsArrayAttribute, ATTRIBUTES)) {
524 			size_t idx = 0;
525 			deserializer.readArray!T((sz){}, {
526 				static if (hasSerializableFields!T) {
527 					switch (idx++) {
528 						default: break;
529 						foreach (i, mname; SerializableFields!T) {
530 							alias TM = typeof(__traits(getMember, ret, mname));
531 							alias TA = TypeTuple!(__traits(getAttributes, __traits(getMember, ret, mname)));
532 							case i:
533 								static if (hasAttribute!(OptionalAttribute, __traits(getMember, T, mname)))
534 									if (deserializer.tryReadNull()) return;
535 								set[i] = true;
536 								__traits(getMember, ret, mname) = deserializeImpl!(TM, Serializer, TA)(deserializer);
537 								break;
538 						}
539 					}
540 				} else {
541 					pragma(msg, "Deserializing composite type "~T.stringof~" which has no serializable fields.");
542 				}
543 			});
544 		} else {
545 			deserializer.readDictionary!T((name) {
546 				static if (hasSerializableFields!T) {
547 					switch (name) {
548 						default: break;
549 						foreach (i, mname; SerializableFields!T) {
550 							alias TM = typeof(__traits(getMember, ret, mname));
551 							alias TA = TypeTuple!(__traits(getAttributes, __traits(getMember, ret, mname)));
552 							enum fname = getAttribute!(T, mname, NameAttribute)(NameAttribute(underscoreStrip(mname))).name;
553 							case fname:
554 								static if (hasAttribute!(OptionalAttribute, __traits(getMember, T, mname)))
555 									if (deserializer.tryReadNull()) return;
556 								set[i] = true;
557 								__traits(getMember, ret, mname) = deserializeImpl!(TM, Policy, Serializer, TA)(deserializer);
558 								break;
559 						}
560 					}
561 				} else {
562 					pragma(msg, "Deserializing composite type "~T.stringof~" which has no serializable fields.");
563 				}
564 			});
565 		}
566 		foreach (i, mname; SerializableFields!T)
567 			static if (!hasAttribute!(OptionalAttribute, __traits(getMember, T, mname)))
568 				enforce(set[i], "Missing non-optional field '"~mname~"' of type '"~T.stringof~"'.");
569 		return ret;
570 	} else static if (isPointer!T) {
571 		if (deserializer.tryReadNull()) return null;
572 		alias PT = PointerTarget!T;
573 		auto ret = new PT;
574 		*ret = deserializeImpl!(PT, Policy, Serializer)(deserializer);
575 		return ret;
576 	} else static if (is(T == bool) || is(T : real) || is(T : long)) {
577 		return to!T(deserializeImpl!(string, Policy, Serializer)(deserializer));
578 	} else static assert(false, "Unsupported serialization type: " ~ T.stringof);
579 }
580 
581 
582 /**
583 	Attribute for overriding the field name during (de-)serialization.
584 */
585 NameAttribute name(string name)
586 {
587 	return NameAttribute(name);
588 }
589 ///
590 unittest {
591 	struct Test {
592 		@name("screen-size") int screenSize;
593 	}
594 }
595 
596 
597 /**
598 	Attribute marking a field as optional during deserialization.
599 */
600 @property OptionalAttribute optional()
601 {
602 	return OptionalAttribute();
603 }
604 ///
605 unittest {
606 	struct Test {
607 		// does not need to be present during deserialization
608 		@optional int screenSize = 100;
609 	}
610 }
611 
612 
613 /**
614 	Attribute for marking non-serialized fields.
615 */
616 @property IgnoreAttribute ignore()
617 {
618 	return IgnoreAttribute();
619 }
620 ///
621 unittest {
622 	struct Test {
623 		// is neither serialized not deserialized
624 		@ignore int screenSize;
625 	}
626 }
627 
628 
629 /**
630 	Attribute for forcing serialization of enum fields by name instead of by value.
631 */
632 @property ByNameAttribute byName()
633 {
634 	return ByNameAttribute();
635 }
636 ///
637 unittest {
638 	enum Color {
639 		red,
640 		green,
641 		blue
642 	}
643 
644 	struct Test {
645 		// serialized as an int (e.g. 1 for Color.green)
646 		Color color;
647 		// serialized as a string (e.g. "green" for Color.green)
648 		@byName Color namedColor;
649 		// serialized as array of ints
650 		Color[] colorArray;
651 		// serialized as array of strings
652 		@byName Color[] namedColorArray;
653 	}
654 }
655 
656 
657 /**
658 	Attribute for representing a struct/class as an array instead of an object.
659 
660 	Usually structs and class objects are serialized as dictionaries mapping
661 	from field name to value. Using this attribute, they will be serialized
662 	as a flat array instead. Note that changing the layout will make any
663 	already serialized data mismatch when this attribute is used.
664 */
665 @property AsArrayAttribute asArray()
666 {
667 	return AsArrayAttribute();
668 }
669 ///
670 unittest {
671 	struct Fields {
672 		int f1;
673 		string f2;
674 		double f3;
675 	}
676 
677 	struct Test {
678 		// serialized as name:value pairs ["f1": int, "f2": string, "f3": double]
679 		Fields object;
680 		// serialized as a sequential list of values [int, string, double]
681 		@asArray Fields array;
682 	}
683 
684 	import dub.internal.vibecompat.data.json;
685 	static assert(is(typeof(serializeToJson(Test()))));
686 }
687 
688 
689 ///
690 enum FieldExistence
691 {
692 	missing,
693 	exists,
694 	defer
695 }
696 
697 /// User defined attribute (not intended for direct use)
698 struct NameAttribute { string name; }
699 /// ditto
700 struct OptionalAttribute {}
701 /// ditto
702 struct IgnoreAttribute {}
703 /// ditto
704 struct ByNameAttribute {}
705 /// ditto
706 struct AsArrayAttribute {}
707 
708 /**
709 	Checks if a given type has a custom serialization representation.
710 
711 	A class or struct type is custom serializable if it defines a pair of
712 	`toRepresentation`/`fromRepresentation` methods. Any class or
713 	struct type that has this trait will be serialized by using the return
714 	value of it's `toRepresentation` method instead of the original value.
715 
716 	This trait has precedence over `isISOExtStringSerializable` and
717 	`isStringSerializable`.
718 */
719 template isCustomSerializable(T)
720 {
721 	enum bool isCustomSerializable = is(typeof(T.init.toRepresentation())) && is(typeof(T.fromRepresentation(T.init.toRepresentation())) == T);
722 }
723 ///
724 unittest {
725 	// represented as a single uint when serialized
726 	static struct S {
727 		ushort x, y;
728 
729 		uint toRepresentation() const { return x + (y << 16); }
730 		static S fromRepresentation(uint i) { return S(i & 0xFFFF, i >> 16); }
731 	}
732 
733 	static assert(isCustomSerializable!S);
734 }
735 
736 
737 /**
738 	Checks if a given type has an ISO extended string serialization representation.
739 
740 	A class or struct type is ISO extended string serializable if it defines a
741 	pair of `toISOExtString`/`fromISOExtString` methods. Any class or
742 	struct type that has this trait will be serialized by using the return
743 	value of it's `toISOExtString` method instead of the original value.
744 
745 	This is mainly useful for supporting serialization of the the date/time
746 	types in `std.datetime`.
747 
748 	This trait has precedence over `isStringSerializable`.
749 */
750 template isISOExtStringSerializable(T)
751 {
752 	enum bool isISOExtStringSerializable = is(typeof(T.init.toISOExtString()) == string) && is(typeof(T.fromISOExtString("")) == T);
753 }
754 ///
755 unittest {
756 	import std.datetime;
757 
758 	static assert(isISOExtStringSerializable!DateTime);
759 	static assert(isISOExtStringSerializable!SysTime);
760 
761 	// represented as an ISO extended string when serialized
762 	static struct S {
763 		// dummy example implementations
764 		string toISOExtString() const { return ""; }
765 		static S fromISOExtString(string s) { return S.init; }
766 	}
767 
768 	static assert(isISOExtStringSerializable!S);
769 }
770 
771 
772 /**
773 	Checks if a given type has a string serialization representation.
774 
775 	A class or struct type is string serializable if it defines a pair of
776 	`toString`/`fromString` methods. Any class or struct type that
777 	has this trait will be serialized by using the return value of it's
778 	`toString` method instead of the original value.
779 */
780 template isStringSerializable(T)
781 {
782 	enum bool isStringSerializable = is(typeof(T.init.toString()) == string) && is(typeof(T.fromString("")) == T);
783 }
784 ///
785 unittest {
786 	import std.conv;
787 
788 	// represented as the boxed value when serialized
789 	static struct Box(T) {
790 		T value;
791 	}
792 
793 	template BoxPol(S)
794 	{
795 		auto toRepresentation(S s) {
796 			return s.value;
797 		}
798 
799 		S fromRepresentation(typeof(S.init.value) v) {
800 			return S(v);
801 		}
802 	}
803 	static assert(isPolicySerializable!(BoxPol, Box!int));
804 }
805 
806 private template DefaultPolicy(T)
807 {
808 }
809 
810 /**
811 	Checks if a given policy supports custom serialization for a given type.
812 
813 	A class or struct type is custom serializable according to a policy if
814 	the policy defines a pair of `toRepresentation`/`fromRepresentation`
815 	functions. Any class or struct type that has this trait for the policy supplied to
816 	`serializeWithPolicy` will be serialized by using the return value of the
817 	policy `toRepresentation` function instead of the original value.
818 
819 	This trait has precedence over `isCustomSerializable`,
820 	`isISOExtStringSerializable` and `isStringSerializable`.
821 
822 	See_Also: `vibe.data.serialization.serializeWithPolicy`
823 */
824 template isPolicySerializable(alias Policy, T)
825 {
826 	enum bool isPolicySerializable = is(typeof(Policy!T.toRepresentation(T.init))) && 
827 		is(typeof(Policy!T.fromRepresentation(Policy!T.toRepresentation(T.init))) == T);
828 }
829 ///
830 unittest {
831 	import std.conv;
832 	
833 	// represented as a string when serialized
834 	static struct S {
835 		int value;
836 		
837 		// dummy example implementations
838 		string toString() const { return value.to!string(); }
839 		static S fromString(string s) { return S(s.to!int()); }
840 	}
841 	
842 	static assert(isStringSerializable!S);
843 }
844 
845 /**
846 	Chains serialization policy.
847 
848 	Constructs a serialization policy that given a type `T` will apply the
849 	first compatible policy `toRepresentation` and `fromRepresentation`
850 	functions. Policies are evaluated left-to-right according to
851 	`isPolicySerializable`.
852 
853 	See_Also: `vibe.data.serialization.serializeWithPolicy`
854 */
855 template ChainedPolicy(alias Primary, Fallbacks...)
856 {
857 	static if (Fallbacks.length == 0) {
858 		alias ChainedPolicy = Primary;
859 	} else {
860 		alias ChainedPolicy = ChainedPolicy!(ChainedPolicyImpl!(Primary, Fallbacks[0]), Fallbacks[1..$]);
861 	}
862 }
863 ///
864 unittest {
865 	import std.conv;
866 	
867 	// To be represented as the boxed value when serialized
868 	static struct Box(T) {
869 		T value;
870 	}
871 	// Also to berepresented as the boxed value when serialized, but has
872 	// a different way to access the value.
873 	static struct Box2(T) {
874 		private T v;
875 		ref T get() {
876 			return v;
877 		}
878 	}
879 	template BoxPol(S)
880 	{
881 		auto toRepresentation(S s) {
882 			return s.value;
883 		}
884 		
885 		S fromRepresentation(typeof(toRepresentation(S.init)) v) {
886 			return S(v);
887 		}
888 	}
889 	template Box2Pol(S)
890 	{
891 		auto toRepresentation(S s) {
892 			return s.get();
893 		}
894 		
895 		S fromRepresentation(typeof(toRepresentation(S.init)) v) {
896 			S s;
897 			s.get() = v;
898 			return s;
899 		}
900 	}
901 	alias ChainPol = ChainedPolicy!(BoxPol, Box2Pol);
902 	static assert(!isPolicySerializable!(BoxPol, Box2!int));
903 	static assert(!isPolicySerializable!(Box2Pol, Box!int));
904 	static assert(isPolicySerializable!(ChainPol, Box!int));
905 	static assert(isPolicySerializable!(ChainPol, Box2!int));
906 }
907 
908 private template ChainedPolicyImpl(alias Primary, alias Fallback)
909 {
910 	template Pol(T)
911 	{
912 		static if (isPolicySerializable!(Primary, T)) {
913 			alias toRepresentation = Primary!T.toRepresentation;
914 			alias fromRepresentation = Primary!T.fromRepresentation;
915 		} else {
916 			alias toRepresentation = Fallback!T.toRepresentation;
917 			alias fromRepresentation = Fallback!T.fromRepresentation;
918 		}
919 	}
920 	alias ChainedPolicyImpl = Pol;
921 }
922 
923 private template hasAttribute(T, alias decl) { enum hasAttribute = findFirstUDA!(T, decl).found; }
924 
925 unittest {
926 	@asArray int i1;
927 	static assert(hasAttribute!(AsArrayAttribute, i1));
928 	int i2;
929 	static assert(!hasAttribute!(AsArrayAttribute, i2));
930 }
931 
932 private template hasAttributeL(T, ATTRIBUTES...) {
933 	static if (ATTRIBUTES.length == 1) {
934 		enum hasAttributeL = is(typeof(ATTRIBUTES[0]) == T);
935 	} else static if (ATTRIBUTES.length > 1) {
936 		enum hasAttributeL = hasAttributeL!(T, ATTRIBUTES[0 .. $/2]) || hasAttributeL!(T, ATTRIBUTES[$/2 .. $]);
937 	} else {
938 		enum hasAttributeL = false;
939 	}
940 }
941 
942 unittest {
943 	static assert(hasAttributeL!(AsArrayAttribute, byName, asArray));
944 	static assert(!hasAttributeL!(AsArrayAttribute, byName));
945 }
946 
947 private static T getAttribute(TT, string mname, T)(T default_value)
948 {
949 	enum val = findFirstUDA!(T, __traits(getMember, TT, mname));
950 	static if (val.found) return val.value;
951 	else return default_value;
952 }
953 
954 private string underscoreStrip(string field_name)
955 {
956 	if( field_name.length < 1 || field_name[$-1] != '_' ) return field_name;
957 	else return field_name[0 .. $-1];
958 }
959 
960 
961 private template hasSerializableFields(T, size_t idx = 0)
962 {
963 	enum hasSerializableFields = SerializableFields!(T).length > 0;
964 	/*static if (idx < __traits(allMembers, T).length) {
965 		enum mname = __traits(allMembers, T)[idx];
966 		static if (!isRWPlainField!(T, mname) && !isRWField!(T, mname)) enum hasSerializableFields = hasSerializableFields!(T, idx+1);
967 		else static if (hasAttribute!(IgnoreAttribute, __traits(getMember, T, mname))) enum hasSerializableFields = hasSerializableFields!(T, idx+1);
968 		else enum hasSerializableFields = true;
969 	} else enum hasSerializableFields = false;*/
970 }
971 
972 private template SerializableFields(COMPOSITE)
973 {
974 	alias SerializableFields = FilterSerializableFields!(COMPOSITE, __traits(allMembers, COMPOSITE));
975 }
976 
977 private template FilterSerializableFields(COMPOSITE, FIELDS...)
978 {
979 	static if (FIELDS.length > 1) {
980 		alias FilterSerializableFields = TypeTuple!(
981 			FilterSerializableFields!(COMPOSITE, FIELDS[0 .. $/2]),
982 			FilterSerializableFields!(COMPOSITE, FIELDS[$/2 .. $]));
983 	} else static if (FIELDS.length == 1) {
984 		alias T = COMPOSITE;
985 		enum mname = FIELDS[0];
986 		static if (isRWPlainField!(T, mname) || isRWField!(T, mname)) {
987 			alias Tup = TypeTuple!(__traits(getMember, COMPOSITE, FIELDS[0]));
988 			static if (Tup.length != 1) {
989 				alias FilterSerializableFields = TypeTuple!(mname);
990 			} else {
991 				static if (!hasAttribute!(IgnoreAttribute, __traits(getMember, T, mname)))
992 					alias FilterSerializableFields = TypeTuple!(mname);
993 				else alias FilterSerializableFields = TypeTuple!();
994 			}
995 		} else alias FilterSerializableFields = TypeTuple!();
996 	} else alias FilterSerializableFields = TypeTuple!();
997 }
998 
999 private size_t getExpandedFieldCount(T, FIELDS...)()
1000 {
1001 	size_t ret = 0;
1002 	foreach (F; FIELDS) ret += TypeTuple!(__traits(getMember, T, F)).length;
1003 	return ret;
1004 }
1005 
1006 /******************************************************************************/
1007 /* General serialization unit testing                                         */
1008 /******************************************************************************/
1009 
1010 version (unittest) {
1011 	private struct TestSerializer {
1012 		import std.array, std.conv, std..string;
1013 
1014 		string result;
1015 
1016 		enum isSupportedValueType(T) = is(T == string) || is(T == typeof(null)) || is(T == float) || is (T == int);
1017 
1018 		string getSerializedResult() { return result; }
1019 		void beginWriteDictionary(T)() { result ~= "D("~T.mangleof~"){"; }
1020 		void endWriteDictionary(T)() { result ~= "}D("~T.mangleof~")"; }
1021 		void beginWriteDictionaryEntry(T)(string name) { result ~= "DE("~T.mangleof~","~name~")("; }
1022 		void endWriteDictionaryEntry(T)(string name) { result ~= ")DE("~T.mangleof~","~name~")"; }
1023 		void beginWriteArray(T)(size_t length) { result ~= "A("~T.mangleof~")["~length.to!string~"]["; }
1024 		void endWriteArray(T)() { result ~= "]A("~T.mangleof~")"; }
1025 		void beginWriteArrayEntry(T)(size_t i) { result ~= "AE("~T.mangleof~","~i.to!string~")("; }
1026 		void endWriteArrayEntry(T)(size_t i) { result ~= ")AE("~T.mangleof~","~i.to!string~")"; }
1027 		void writeValue(T)(T value) {
1028 			if (is(T == typeof(null))) result ~= "null";
1029 			else {
1030 				assert(isSupportedValueType!T);
1031 				result ~= "V("~T.mangleof~")("~value.to!string~")";
1032 			}
1033 		}
1034 
1035 		// deserialization
1036 		void readDictionary(T)(scope void delegate(string) entry_callback)
1037 		{
1038 			skip("D("~T.mangleof~"){");
1039 			while (result.startsWith("DE(")) {
1040 				result = result[3 .. $];
1041 				auto idx = result.indexOf(',');
1042 				auto idx2 = result.indexOf(")(");
1043 				assert(idx > 0 && idx2 > idx);
1044 				auto t = result[0 .. idx];
1045 				auto n = result[idx+1 .. idx2];
1046 				result = result[idx2+2 .. $];
1047 				entry_callback(n);
1048 				skip(")DE("~t~","~n~")");
1049 			}
1050 			skip("}D("~T.mangleof~")");
1051 		}
1052 
1053 		void readArray(T)(scope void delegate(size_t) size_callback, scope void delegate() entry_callback)
1054 		{
1055 			skip("A("~T.mangleof~")[");
1056 			auto bidx = result.indexOf("][");
1057 			assert(bidx > 0);
1058 			auto cnt = result[0 .. bidx].to!size_t;
1059 			result = result[bidx+2 .. $];
1060 
1061 			size_t i = 0;
1062 			while (result.startsWith("AE(")) {
1063 				result = result[3 .. $];
1064 				auto idx = result.indexOf(',');
1065 				auto idx2 = result.indexOf(")(");
1066 				assert(idx > 0 && idx2 > idx);
1067 				auto t = result[0 .. idx];
1068 				auto n = result[idx+1 .. idx2];
1069 				result = result[idx2+2 .. $];
1070 				assert(n == i.to!string);
1071 				entry_callback();
1072 				skip(")AE("~t~","~n~")");
1073 				i++;
1074 			}
1075 			skip("]A("~T.mangleof~")");
1076 
1077 			assert(i == cnt);
1078 		}
1079 
1080 		T readValue(T)()
1081 		{
1082 			skip("V("~T.mangleof~")(");
1083 			auto idx = result.indexOf(')');
1084 			assert(idx >= 0);
1085 			auto ret = result[0 .. idx].to!T;
1086 			result = result[idx+1 .. $];
1087 			return ret;
1088 		}
1089 
1090 		void skip(string prefix)
1091 		{
1092 			assert(result.startsWith(prefix), result);
1093 			result = result[prefix.length .. $];
1094 		}
1095 
1096 		bool tryReadNull()
1097 		{
1098 			if (result.startsWith("null")) {
1099 				result = result[4 .. $];
1100 				return true;
1101 			} else return false;
1102 		}
1103 	}
1104 }
1105 
1106 unittest { // basic serialization behavior
1107 	import std.typecons : Nullable;
1108 
1109 	static void test(T)(T value, string expected) {
1110 		assert(serialize!TestSerializer(value) == expected, serialize!TestSerializer(value));
1111 		static if (isPointer!T) {
1112 			if (value) assert(*deserialize!(TestSerializer, T)(expected) == *value);
1113 			else assert(deserialize!(TestSerializer, T)(expected) is null);
1114 		} else static if (is(T == Nullable!U, U)) {
1115 			if (value.isNull()) assert(deserialize!(TestSerializer, T)(expected).isNull);
1116 			else assert(deserialize!(TestSerializer, T)(expected) == value);
1117 		} else assert(deserialize!(TestSerializer, T)(expected) == value);
1118 	}
1119 
1120 	test("hello", "V(Aya)(hello)");
1121 	test(12, "V(i)(12)");
1122 	test(12.0, "V(Aya)(12)");
1123 	test(12.0f, "V(f)(12)");
1124 	assert(serialize!TestSerializer(null) ==  "null");
1125 	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)");
1126 	test(["hello": "world"], "D(HAyaAya){DE(Aya,hello)(V(Aya)(world))DE(Aya,hello)}D(HAyaAya)");
1127 	test(cast(int*)null, "null");
1128 	int i = 42;
1129 	test(&i, "V(i)(42)");
1130 	Nullable!int j;
1131 	test(j, "null");
1132 	j = 42;
1133 	test(j, "V(i)(42)");
1134 }
1135 
1136 unittest { // basic user defined types
1137 	static struct S { string f; }
1138 	enum Sm = S.mangleof;
1139 	auto s = S("hello");
1140 	enum s_ser = "D("~Sm~"){DE(Aya,f)(V(Aya)(hello))DE(Aya,f)}D("~Sm~")";
1141 	assert(serialize!TestSerializer(s) == s_ser, serialize!TestSerializer(s));
1142 	assert(deserialize!(TestSerializer, S)(s_ser) == s);
1143 
1144 	static class C { string f; }
1145 	enum Cm = C.mangleof;
1146 	C c;
1147 	assert(serialize!TestSerializer(c) == "null");
1148 	c = new C;
1149 	c.f = "hello";
1150 	enum c_ser = "D("~Cm~"){DE(Aya,f)(V(Aya)(hello))DE(Aya,f)}D("~Cm~")";
1151 	assert(serialize!TestSerializer(c) == c_ser);
1152 	assert(deserialize!(TestSerializer, C)(c_ser).f == c.f);
1153 
1154 	enum E { hello, world }
1155 	assert(serialize!TestSerializer(E.hello) == "V(i)(0)");
1156 	assert(serialize!TestSerializer(E.world) == "V(i)(1)");
1157 }
1158 
1159 unittest { // tuple serialization
1160 	import std.typecons : Tuple;
1161 
1162 	static struct S(T...) { T f; }
1163 	enum Sm = S!(int, string).mangleof;
1164 	enum Tum = Tuple!(int, string).mangleof;
1165 	auto s = S!(int, string)(42, "hello");
1166 	assert(serialize!TestSerializer(s) ==
1167 		"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~")");
1168 
1169 	static struct T { @asArray S!(int, string) g; }
1170 	enum Tm = T.mangleof;
1171 	auto t = T(s);
1172 	assert(serialize!TestSerializer(t) ==
1173 		"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~")");
1174 }
1175 
1176 unittest { // testing the various UDAs
1177 	enum E { hello, world }
1178 	enum Em = E.mangleof;
1179 	static struct S {
1180 		@byName E e;
1181 		@ignore int i;
1182 		@optional float f;
1183 	}
1184 	enum Sm = S.mangleof;
1185 	auto s = S(E.world, 42, 1.0f);
1186 	assert(serialize!TestSerializer(s) ==
1187 		"D("~Sm~"){DE("~Em~",e)(V(Aya)(world))DE("~Em~",e)DE(f,f)(V(f)(1))DE(f,f)}D("~Sm~")");
1188 }
1189 
1190 unittest { // custom serialization support
1191 	// iso-ext
1192 	import std.datetime;
1193 	auto t = TimeOfDay(6, 31, 23);
1194 	assert(serialize!TestSerializer(t) == "V(Aya)(06:31:23)");
1195 	auto d = Date(1964, 1, 23);
1196 	assert(serialize!TestSerializer(d) == "V(Aya)(1964-01-23)");
1197 	auto dt = DateTime(d, t);
1198 	assert(serialize!TestSerializer(dt) == "V(Aya)(1964-01-23T06:31:23)");
1199 	auto st = SysTime(dt, UTC());
1200 	assert(serialize!TestSerializer(st) == "V(Aya)(1964-01-23T06:31:23Z)");
1201 
1202 	// string
1203 	struct S1 { int i; string toString() const { return "hello"; } static S1 fromString(string) { return S1.init; } }
1204 	struct S2 { int i; string toString() const { return "hello"; } }
1205 	enum S2m = S2.mangleof;
1206 	struct S3 { int i; static S3 fromString(string) { return S3.init; } }
1207 	enum S3m = S3.mangleof;
1208 	assert(serialize!TestSerializer(S1.init) == "V(Aya)(hello)");
1209 	assert(serialize!TestSerializer(S2.init) == "D("~S2m~"){DE(i,i)(V(i)(0))DE(i,i)}D("~S2m~")");
1210 	assert(serialize!TestSerializer(S3.init) == "D("~S3m~"){DE(i,i)(V(i)(0))DE(i,i)}D("~S3m~")");
1211 
1212 	// custom
1213 	struct C1 { int i; float toRepresentation() const { return 1.0f; } static C1 fromRepresentation(float f) { return C1.init; } }
1214 	struct C2 { int i; float toRepresentation() const { return 1.0f; } }
1215 	enum C2m = C2.mangleof;
1216 	struct C3 { int i; static C3 fromRepresentation(float f) { return C3.init; } }
1217 	enum C3m = C3.mangleof;
1218 	assert(serialize!TestSerializer(C1.init) == "V(f)(1)");
1219 	assert(serialize!TestSerializer(C2.init) == "D("~C2m~"){DE(i,i)(V(i)(0))DE(i,i)}D("~C2m~")");
1220 	assert(serialize!TestSerializer(C3.init) == "D("~C3m~"){DE(i,i)(V(i)(0))DE(i,i)}D("~C3m~")");
1221 }
1222 
1223 unittest // Testing corner case: member function returning by ref
1224 {
1225 	import dub.internal.vibecompat.data.json;
1226 
1227 	static struct S
1228 	{
1229 		int i;
1230 		ref int foo() { return i; }
1231 	}
1232 
1233 	static assert(__traits(compiles, { S().serializeToJson(); }));
1234 	static assert(__traits(compiles, { Json().deserializeJson!S(); }));
1235 
1236 	auto s = S(1);
1237 	assert(s.serializeToJson().deserializeJson!S() == s);
1238 }
1239 
1240 unittest // Testing corner case: Variadic template constructors and methods
1241 {
1242 	import dub.internal.vibecompat.data.json;
1243 
1244 	static struct S
1245 	{
1246 		int i;
1247 		this(Args...)(Args args) {}
1248 		int foo(Args...)(Args args) { return i; }
1249 		ref int bar(Args...)(Args args) { return i; }
1250 	}
1251 
1252 	static assert(__traits(compiles, { S().serializeToJson(); }));
1253 	static assert(__traits(compiles, { Json().deserializeJson!S(); }));
1254 
1255 	auto s = S(1);
1256 	assert(s.serializeToJson().deserializeJson!S() == s);
1257 }
1258 
1259 unittest // Make sure serializing through properties still works
1260 {
1261 	import dub.internal.vibecompat.data.json;
1262 
1263 	static struct S
1264 	{
1265 		public int i;
1266 		private int privateJ;
1267 
1268 		@property int j() { return privateJ; }
1269 		@property void j(int j) { privateJ = j; }
1270 	}
1271 
1272 	auto s = S(1, 2);
1273 	assert(s.serializeToJson().deserializeJson!S() == s);
1274 }
1275 
1276 unittest { // test BitFlags serialization
1277 	import std.typecons : BitFlags;
1278 
1279 	enum Flag {
1280 		a = 1<<0,
1281 		b = 1<<1,
1282 		c = 1<<2
1283 	}
1284 	enum Flagm = Flag.mangleof;
1285 
1286 	alias Flags = BitFlags!Flag;
1287 	enum Flagsm = Flags.mangleof;
1288 
1289 	enum Fi_ser = "A(A"~Flagm~")[0][]A(A"~Flagm~")";
1290 	assert(serialize!TestSerializer(Flags.init) == Fi_ser);
1291 
1292 	enum Fac_ser = "A(A"~Flagm~")[2][AE("~Flagm~",0)(V(i)(1))AE("~Flagm~",0)AE("~Flagm~",1)(V(i)(4))AE("~Flagm~",1)]A(A"~Flagm~")";
1293 	assert(serialize!TestSerializer(Flags(Flag.a, Flag.c)) == Fac_ser);
1294 
1295 	struct S { @byName Flags f; }
1296 	enum Sm = S.mangleof;
1297 	enum Sac_ser = "D("~Sm~"){DE("~Flagsm~",f)(A(A"~Flagm~")[2][AE("~Flagm~",0)(V(Aya)(a))AE("~Flagm~",0)AE("~Flagm~",1)(V(Aya)(c))AE("~Flagm~",1)]A(A"~Flagm~"))DE("~Flagsm~",f)}D("~Sm~")";
1298 
1299 	assert(serialize!TestSerializer(S(Flags(Flag.a, Flag.c))) == Sac_ser);
1300 
1301 	assert(deserialize!(TestSerializer, Flags)(Fi_ser) == Flags.init);
1302 	assert(deserialize!(TestSerializer, Flags)(Fac_ser) == Flags(Flag.a, Flag.c));
1303 	assert(deserialize!(TestSerializer, S)(Sac_ser) == S(Flags(Flag.a, Flag.c)));
1304 }