Newer
Older
dub_jkp / source / dub / internal / vibecompat / data / serialization.d
  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_data) 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 : Nullable, Tuple, tuple;
  295. import std.typecons : BitFlags;
  296.  
  297. static assert(Serializer.isSupportedValueType!string, "All serializers must support string values.");
  298. static assert(Serializer.isSupportedValueType!(typeof(null)), "All serializers must support null values.");
  299.  
  300. alias TU = Unqual!T;
  301.  
  302. static if (is(TU == enum)) {
  303. static if (hasAttributeL!(ByNameAttribute, ATTRIBUTES)) {
  304. serializeImpl!(Serializer, Policy, string)(serializer, value.to!string());
  305. } else {
  306. serializeImpl!(Serializer, Policy, OriginalType!TU)(serializer, cast(OriginalType!TU)value);
  307. }
  308. } else static if (Serializer.isSupportedValueType!TU) {
  309. static if (is(TU == typeof(null))) serializer.writeValue!TU(null);
  310. else serializer.writeValue!TU(value);
  311. } else static if (/*isInstanceOf!(Tuple, TU)*/is(T == Tuple!TPS, TPS...)) {
  312. static if (TU.Types.length == 1) {
  313. serializeImpl!(Serializer, Policy, typeof(value[0]), ATTRIBUTES)(serializer, value[0]);
  314. } else {
  315. serializer.beginWriteArray!TU(value.length);
  316. foreach (i, TV; T.Types) {
  317. serializer.beginWriteArrayEntry!TV(i);
  318. serializeImpl!(Serializer, Policy, TV, ATTRIBUTES)(serializer, value[i]);
  319. serializer.endWriteArrayEntry!TV(i);
  320. }
  321. serializer.endWriteArray!TU();
  322. }
  323. } else static if (isArray!TU) {
  324. alias TV = typeof(value[0]);
  325. serializer.beginWriteArray!TU(value.length);
  326. foreach (i, ref el; value) {
  327. serializer.beginWriteArrayEntry!TV(i);
  328. serializeImpl!(Serializer, Policy, TV, ATTRIBUTES)(serializer, el);
  329. serializer.endWriteArrayEntry!TV(i);
  330. }
  331. serializer.endWriteArray!TU();
  332. } else static if (isAssociativeArray!TU) {
  333. alias TK = KeyType!TU;
  334. alias TV = ValueType!TU;
  335. static if (__traits(compiles, serializer.beginWriteDictionary!TU(0))) {
  336. auto nfields = value.length;
  337. serializer.beginWriteDictionary!TU(nfields);
  338. } else {
  339. serializer.beginWriteDictionary!TU();
  340. }
  341. foreach (key, ref el; value) {
  342. string keyname;
  343. static if (is(TK : string)) keyname = key;
  344. else static if (is(TK : real) || is(TK : long) || is(TK == enum)) keyname = key.to!string;
  345. else static if (isStringSerializable!TK) keyname = key.toString();
  346. else static assert(false, "Associative array keys must be strings, numbers, enums, or have toString/fromString methods.");
  347. serializer.beginWriteDictionaryEntry!TV(keyname);
  348. serializeImpl!(Serializer, Policy, TV, ATTRIBUTES)(serializer, el);
  349. serializer.endWriteDictionaryEntry!TV(keyname);
  350. }
  351. static if (__traits(compiles, serializer.endWriteDictionary!TU(0))) {
  352. serializer.endWriteDictionary!TU(nfields);
  353. } else {
  354. serializer.endWriteDictionary!TU();
  355. }
  356. } else static if (/*isInstanceOf!(Nullable, TU)*/is(T == Nullable!TPS, TPS...)) {
  357. if (value.isNull()) serializeImpl!(Serializer, Policy, typeof(null))(serializer, null);
  358. else serializeImpl!(Serializer, Policy, typeof(value.get()), ATTRIBUTES)(serializer, value.get());
  359. } else static if (is(T == BitFlags!E, E)) {
  360. size_t cnt = 0;
  361. foreach (v; EnumMembers!E)
  362. if (value & v)
  363. cnt++;
  364.  
  365. serializer.beginWriteArray!(E[])(cnt);
  366. cnt = 0;
  367. foreach (v; EnumMembers!E)
  368. if (value & v) {
  369. serializer.beginWriteArrayEntry!E(cnt);
  370. serializeImpl!(Serializer, Policy, E, ATTRIBUTES)(serializer, v);
  371. serializer.endWriteArrayEntry!E(cnt);
  372. cnt++;
  373. }
  374. serializer.endWriteArray!(E[])();
  375. } else static if (isPolicySerializable!(Policy, TU)) {
  376. alias CustomType = typeof(Policy!TU.toRepresentation(TU.init));
  377. serializeImpl!(Serializer, Policy, CustomType, ATTRIBUTES)(serializer, Policy!TU.toRepresentation(value));
  378. } else static if (isCustomSerializable!TU) {
  379. alias CustomType = typeof(T.init.toRepresentation());
  380. serializeImpl!(Serializer, Policy, CustomType, ATTRIBUTES)(serializer, value.toRepresentation());
  381. } else static if (isISOExtStringSerializable!TU) {
  382. serializer.writeValue(value.toISOExtString());
  383. } else static if (isStringSerializable!TU) {
  384. serializer.writeValue(value.toString());
  385. } else static if (is(TU == struct) || is(TU == class)) {
  386. static if (!hasSerializableFields!TU)
  387. pragma(msg, "Serializing composite type "~T.stringof~" which has no serializable fields");
  388. static if (is(TU == class)) {
  389. if (value is null) {
  390. serializeImpl!(Serializer, Policy, typeof(null))(serializer, null);
  391. return;
  392. }
  393. }
  394. static if (hasAttributeL!(AsArrayAttribute, ATTRIBUTES)) {
  395. enum nfields = getExpandedFieldCount!(TU, SerializableFields!TU);
  396. serializer.beginWriteArray!TU(nfields);
  397. foreach (mname; SerializableFields!TU) {
  398. alias TMS = TypeTuple!(typeof(__traits(getMember, value, mname)));
  399. foreach (j, TM; TMS) {
  400. alias TA = TypeTuple!(__traits(getAttributes, TypeTuple!(__traits(getMember, T, mname))[j]));
  401. serializer.beginWriteArrayEntry!TM(j);
  402. serializeImpl!(Serializer, Policy, TM, TA)(serializer, tuple(__traits(getMember, value, mname))[j]);
  403. serializer.endWriteArrayEntry!TM(j);
  404. }
  405. }
  406. serializer.endWriteArray!TU();
  407. } else {
  408. static if (__traits(compiles, serializer.beginWriteDictionary!TU(0))) {
  409. enum nfields = getExpandedFieldCount!(TU, SerializableFields!TU);
  410. serializer.beginWriteDictionary!TU(nfields);
  411. } else {
  412. serializer.beginWriteDictionary!TU();
  413. }
  414. foreach (mname; SerializableFields!TU) {
  415. alias TM = TypeTuple!(typeof(__traits(getMember, value, mname)));
  416. static if (TM.length == 1) {
  417. alias TA = TypeTuple!(__traits(getAttributes, __traits(getMember, T, mname)));
  418. enum name = getAttribute!(TU, mname, NameAttribute)(NameAttribute(underscoreStrip(mname))).name;
  419. auto vt = __traits(getMember, value, mname);
  420. serializer.beginWriteDictionaryEntry!(typeof(vt))(name);
  421. serializeImpl!(Serializer, Policy, typeof(vt), TA)(serializer, vt);
  422. serializer.endWriteDictionaryEntry!(typeof(vt))(name);
  423. } else {
  424. alias TA = TypeTuple!(); // FIXME: support attributes for tuples somehow
  425. enum name = underscoreStrip(mname);
  426. auto vt = tuple(__traits(getMember, value, mname));
  427. serializer.beginWriteDictionaryEntry!(typeof(vt))(name);
  428. serializeImpl!(Serializer, Policy, typeof(vt), TA)(serializer, vt);
  429. serializer.endWriteDictionaryEntry!(typeof(vt))(name);
  430. }
  431. }
  432. static if (__traits(compiles, serializer.endWriteDictionary!TU(0))) {
  433. serializer.endWriteDictionary!TU(nfields);
  434. } else {
  435. serializer.endWriteDictionary!TU();
  436. }
  437. }
  438. } else static if (isPointer!TU) {
  439. if (value is null) {
  440. serializer.writeValue(null);
  441. return;
  442. }
  443. serializeImpl!(Serializer, Policy, PointerTarget!TU)(serializer, *value);
  444. } else static if (is(TU == bool) || is(TU : real) || is(TU : long)) {
  445. serializeImpl!(Serializer, Policy, string)(serializer, to!string(value));
  446. } else static assert(false, "Unsupported serialization type: " ~ T.stringof);
  447. }
  448.  
  449.  
  450. private T deserializeImpl(T, alias Policy, Serializer, ATTRIBUTES...)(ref Serializer deserializer)
  451. {
  452. import std.typecons : Nullable;
  453. import std.typecons : BitFlags;
  454.  
  455. static assert(Serializer.isSupportedValueType!string, "All serializers must support string values.");
  456. static assert(Serializer.isSupportedValueType!(typeof(null)), "All serializers must support null values.");
  457.  
  458. static if (is(T == enum)) {
  459. static if (hasAttributeL!(ByNameAttribute, ATTRIBUTES)) {
  460. return deserializeImpl!(string, Policy, Serializer)(deserializer).to!T();
  461. } else {
  462. return cast(T)deserializeImpl!(OriginalType!T, Policy, Serializer)(deserializer);
  463. }
  464. } else static if (Serializer.isSupportedValueType!T) {
  465. return deserializer.readValue!T();
  466. } else static if (isStaticArray!T) {
  467. alias TV = typeof(T.init[0]);
  468. T ret;
  469. size_t i = 0;
  470. deserializer.readArray!T((sz) { assert(sz == 0 || sz == T.length); }, {
  471. assert(i < T.length);
  472. ret[i++] = deserializeImpl!(TV, Policy, Serializer, ATTRIBUTES)(deserializer);
  473. });
  474. return ret;
  475. } else static if (isDynamicArray!T) {
  476. alias TV = typeof(T.init[0]);
  477. //auto ret = appender!T();
  478. T ret; // Cannot use appender because of DMD BUG 10690/10859/11357
  479. deserializer.readArray!T((sz) { ret.reserve(sz); }, () {
  480. ret ~= deserializeImpl!(TV, Policy, Serializer, ATTRIBUTES)(deserializer);
  481. });
  482. return ret;//cast(T)ret.data;
  483. } else static if (isAssociativeArray!T) {
  484. alias TK = KeyType!T;
  485. alias TV = ValueType!T;
  486. T ret;
  487. deserializer.readDictionary!T((name) {
  488. TK key;
  489. static if (is(TK == string)) key = name;
  490. else static if (is(TK : real) || is(TK : long) || is(TK == enum)) key = name.to!TK;
  491. else static if (isStringSerializable!TK) key = TK.fromString(name);
  492. else static assert(false, "Associative array keys must be strings, numbers, enums, or have toString/fromString methods.");
  493. ret[key] = deserializeImpl!(TV, Policy, Serializer, ATTRIBUTES)(deserializer);
  494. });
  495. return ret;
  496. } else static if (isInstanceOf!(Nullable, T)) {
  497. if (deserializer.tryReadNull()) return T.init;
  498. return T(deserializeImpl!(typeof(T.init.get()), Policy, Serializer, ATTRIBUTES)(deserializer));
  499. } else static if (is(T == BitFlags!E, E)) {
  500. T ret;
  501. deserializer.readArray!(E[])((sz) {}, {
  502. ret |= deserializeImpl!(E, Policy, Serializer, ATTRIBUTES)(deserializer);
  503. });
  504. return ret;
  505. } else static if (isPolicySerializable!(Policy, T)) {
  506. alias CustomType = typeof(Policy!T.toRepresentation(T.init));
  507. return Policy!T.fromRepresentation(deserializeImpl!(CustomType, Policy, Serializer, ATTRIBUTES)(deserializer));
  508. } else static if (isCustomSerializable!T) {
  509. alias CustomType = typeof(T.init.toRepresentation());
  510. return T.fromRepresentation(deserializeImpl!(CustomType, Policy, Serializer, ATTRIBUTES)(deserializer));
  511. } else static if (isISOExtStringSerializable!T) {
  512. return T.fromISOExtString(deserializer.readValue!string());
  513. } else static if (isStringSerializable!T) {
  514. return T.fromString(deserializer.readValue!string());
  515. } else static if (is(T == struct) || is(T == class)) {
  516. static if (is(T == class)) {
  517. if (deserializer.tryReadNull()) return null;
  518. }
  519.  
  520. bool[__traits(allMembers, T).length] set;
  521. string name;
  522. T ret;
  523. static if (is(T == class)) ret = new T;
  524.  
  525. static if (hasAttributeL!(AsArrayAttribute, ATTRIBUTES)) {
  526. size_t idx = 0;
  527. deserializer.readArray!T((sz){}, {
  528. static if (hasSerializableFields!T) {
  529. switch (idx++) {
  530. default: break;
  531. foreach (i, mname; SerializableFields!T) {
  532. alias TM = typeof(__traits(getMember, ret, mname));
  533. alias TA = TypeTuple!(__traits(getAttributes, __traits(getMember, ret, mname)));
  534. case i:
  535. static if (hasAttribute!(OptionalAttribute, __traits(getMember, T, mname)))
  536. if (deserializer.tryReadNull()) return;
  537. set[i] = true;
  538. __traits(getMember, ret, mname) = deserializeImpl!(TM, Serializer, TA)(deserializer);
  539. break;
  540. }
  541. }
  542. } else {
  543. pragma(msg, "Deserializing composite type "~T.stringof~" which has no serializable fields.");
  544. }
  545. });
  546. } else {
  547. deserializer.readDictionary!T((name) {
  548. static if (hasSerializableFields!T) {
  549. switch (name) {
  550. default: break;
  551. foreach (i, mname; SerializableFields!T) {
  552. alias TM = typeof(__traits(getMember, ret, mname));
  553. alias TA = TypeTuple!(__traits(getAttributes, __traits(getMember, ret, mname)));
  554. enum fname = getAttribute!(T, mname, NameAttribute)(NameAttribute(underscoreStrip(mname))).name;
  555. case fname:
  556. static if (hasAttribute!(OptionalAttribute, __traits(getMember, T, mname)))
  557. if (deserializer.tryReadNull()) return;
  558. set[i] = true;
  559. __traits(getMember, ret, mname) = deserializeImpl!(TM, Policy, Serializer, TA)(deserializer);
  560. break;
  561. }
  562. }
  563. } else {
  564. pragma(msg, "Deserializing composite type "~T.stringof~" which has no serializable fields.");
  565. }
  566. });
  567. }
  568. foreach (i, mname; SerializableFields!T)
  569. static if (!hasAttribute!(OptionalAttribute, __traits(getMember, T, mname)))
  570. enforce(set[i], "Missing non-optional field '"~mname~"' of type '"~T.stringof~"'.");
  571. return ret;
  572. } else static if (isPointer!T) {
  573. if (deserializer.tryReadNull()) return null;
  574. alias PT = PointerTarget!T;
  575. auto ret = new PT;
  576. *ret = deserializeImpl!(PT, Policy, Serializer)(deserializer);
  577. return ret;
  578. } else static if (is(T == bool) || is(T : real) || is(T : long)) {
  579. return to!T(deserializeImpl!(string, Policy, Serializer)(deserializer));
  580. } else static assert(false, "Unsupported serialization type: " ~ T.stringof);
  581. }
  582.  
  583.  
  584. /**
  585. Attribute for overriding the field name during (de-)serialization.
  586. */
  587. NameAttribute name(string name)
  588. {
  589. return NameAttribute(name);
  590. }
  591. ///
  592. unittest {
  593. struct Test {
  594. @name("screen-size") int screenSize;
  595. }
  596. }
  597.  
  598.  
  599. /**
  600. Attribute marking a field as optional during deserialization.
  601. */
  602. @property OptionalAttribute optional()
  603. {
  604. return OptionalAttribute();
  605. }
  606. ///
  607. unittest {
  608. struct Test {
  609. // does not need to be present during deserialization
  610. @optional int screenSize = 100;
  611. }
  612. }
  613.  
  614.  
  615. /**
  616. Attribute for marking non-serialized fields.
  617. */
  618. @property IgnoreAttribute ignore()
  619. {
  620. return IgnoreAttribute();
  621. }
  622. ///
  623. unittest {
  624. struct Test {
  625. // is neither serialized not deserialized
  626. @ignore int screenSize;
  627. }
  628. }
  629.  
  630.  
  631. /**
  632. Attribute for forcing serialization of enum fields by name instead of by value.
  633. */
  634. @property ByNameAttribute byName()
  635. {
  636. return ByNameAttribute();
  637. }
  638. ///
  639. unittest {
  640. enum Color {
  641. red,
  642. green,
  643. blue
  644. }
  645.  
  646. struct Test {
  647. // serialized as an int (e.g. 1 for Color.green)
  648. Color color;
  649. // serialized as a string (e.g. "green" for Color.green)
  650. @byName Color namedColor;
  651. // serialized as array of ints
  652. Color[] colorArray;
  653. // serialized as array of strings
  654. @byName Color[] namedColorArray;
  655. }
  656. }
  657.  
  658.  
  659. /**
  660. Attribute for representing a struct/class as an array instead of an object.
  661.  
  662. Usually structs and class objects are serialized as dictionaries mapping
  663. from field name to value. Using this attribute, they will be serialized
  664. as a flat array instead. Note that changing the layout will make any
  665. already serialized data mismatch when this attribute is used.
  666. */
  667. @property AsArrayAttribute asArray()
  668. {
  669. return AsArrayAttribute();
  670. }
  671. ///
  672. unittest {
  673. struct Fields {
  674. int f1;
  675. string f2;
  676. double f3;
  677. }
  678.  
  679. struct Test {
  680. // serialized as name:value pairs ["f1": int, "f2": string, "f3": double]
  681. Fields object;
  682. // serialized as a sequential list of values [int, string, double]
  683. @asArray Fields array;
  684. }
  685.  
  686. import dub.internal.vibecompat.data.json;
  687. static assert(is(typeof(serializeToJson(Test()))));
  688. }
  689.  
  690.  
  691. ///
  692. enum FieldExistence
  693. {
  694. missing,
  695. exists,
  696. defer
  697. }
  698.  
  699. /// User defined attribute (not intended for direct use)
  700. struct NameAttribute { string name; }
  701. /// ditto
  702. struct OptionalAttribute {}
  703. /// ditto
  704. struct IgnoreAttribute {}
  705. /// ditto
  706. struct ByNameAttribute {}
  707. /// ditto
  708. struct AsArrayAttribute {}
  709.  
  710. /**
  711. Checks if a given type has a custom serialization representation.
  712.  
  713. A class or struct type is custom serializable if it defines a pair of
  714. `toRepresentation`/`fromRepresentation` methods. Any class or
  715. struct type that has this trait will be serialized by using the return
  716. value of it's `toRepresentation` method instead of the original value.
  717.  
  718. This trait has precedence over `isISOExtStringSerializable` and
  719. `isStringSerializable`.
  720. */
  721. template isCustomSerializable(T)
  722. {
  723. enum bool isCustomSerializable = is(typeof(T.init.toRepresentation())) && is(typeof(T.fromRepresentation(T.init.toRepresentation())) == T);
  724. }
  725. ///
  726. unittest {
  727. // represented as a single uint when serialized
  728. static struct S {
  729. ushort x, y;
  730.  
  731. uint toRepresentation() const { return x + (y << 16); }
  732. static S fromRepresentation(uint i) { return S(i & 0xFFFF, i >> 16); }
  733. }
  734.  
  735. static assert(isCustomSerializable!S);
  736. }
  737.  
  738.  
  739. /**
  740. Checks if a given type has an ISO extended string serialization representation.
  741.  
  742. A class or struct type is ISO extended string serializable if it defines a
  743. pair of `toISOExtString`/`fromISOExtString` methods. Any class or
  744. struct type that has this trait will be serialized by using the return
  745. value of it's `toISOExtString` method instead of the original value.
  746.  
  747. This is mainly useful for supporting serialization of the the date/time
  748. types in `std.datetime`.
  749.  
  750. This trait has precedence over `isStringSerializable`.
  751. */
  752. template isISOExtStringSerializable(T)
  753. {
  754. enum bool isISOExtStringSerializable = is(typeof(T.init.toISOExtString()) == string) && is(typeof(T.fromISOExtString("")) == T);
  755. }
  756. ///
  757. unittest {
  758. import std.datetime;
  759.  
  760. static assert(isISOExtStringSerializable!DateTime);
  761. static assert(isISOExtStringSerializable!SysTime);
  762.  
  763. // represented as an ISO extended string when serialized
  764. static struct S {
  765. // dummy example implementations
  766. string toISOExtString() const { return ""; }
  767. static S fromISOExtString(string s) { return S.init; }
  768. }
  769.  
  770. static assert(isISOExtStringSerializable!S);
  771. }
  772.  
  773.  
  774. /**
  775. Checks if a given type has a string serialization representation.
  776.  
  777. A class or struct type is string serializable if it defines a pair of
  778. `toString`/`fromString` methods. Any class or struct type that
  779. has this trait will be serialized by using the return value of it's
  780. `toString` method instead of the original value.
  781. */
  782. template isStringSerializable(T)
  783. {
  784. enum bool isStringSerializable = is(typeof(T.init.toString()) == string) && is(typeof(T.fromString("")) == T);
  785. }
  786. ///
  787. unittest {
  788. import std.conv;
  789.  
  790. // represented as the boxed value when serialized
  791. static struct Box(T) {
  792. T value;
  793. }
  794.  
  795. template BoxPol(S)
  796. {
  797. auto toRepresentation(S s) {
  798. return s.value;
  799. }
  800.  
  801. S fromRepresentation(typeof(S.init.value) v) {
  802. return S(v);
  803. }
  804. }
  805. static assert(isPolicySerializable!(BoxPol, Box!int));
  806. }
  807.  
  808. private template DefaultPolicy(T)
  809. {
  810. }
  811.  
  812. /**
  813. Checks if a given policy supports custom serialization for a given type.
  814.  
  815. A class or struct type is custom serializable according to a policy if
  816. the policy defines a pair of `toRepresentation`/`fromRepresentation`
  817. functions. Any class or struct type that has this trait for the policy supplied to
  818. `serializeWithPolicy` will be serialized by using the return value of the
  819. policy `toRepresentation` function instead of the original value.
  820.  
  821. This trait has precedence over `isCustomSerializable`,
  822. `isISOExtStringSerializable` and `isStringSerializable`.
  823.  
  824. See_Also: `vibe.data.serialization.serializeWithPolicy`
  825. */
  826. template isPolicySerializable(alias Policy, T)
  827. {
  828. enum bool isPolicySerializable = is(typeof(Policy!T.toRepresentation(T.init))) &&
  829. is(typeof(Policy!T.fromRepresentation(Policy!T.toRepresentation(T.init))) == T);
  830. }
  831. ///
  832. unittest {
  833. import std.conv;
  834.  
  835. // represented as a string when serialized
  836. static struct S {
  837. int value;
  838.  
  839. // dummy example implementations
  840. string toString() const { return value.to!string(); }
  841. static S fromString(string s) { return S(s.to!int()); }
  842. }
  843.  
  844. static assert(isStringSerializable!S);
  845. }
  846.  
  847. /**
  848. Chains serialization policy.
  849.  
  850. Constructs a serialization policy that given a type `T` will apply the
  851. first compatible policy `toRepresentation` and `fromRepresentation`
  852. functions. Policies are evaluated left-to-right according to
  853. `isPolicySerializable`.
  854.  
  855. See_Also: `vibe.data.serialization.serializeWithPolicy`
  856. */
  857. template ChainedPolicy(alias Primary, Fallbacks...)
  858. {
  859. static if (Fallbacks.length == 0) {
  860. alias ChainedPolicy = Primary;
  861. } else {
  862. alias ChainedPolicy = ChainedPolicy!(ChainedPolicyImpl!(Primary, Fallbacks[0]), Fallbacks[1..$]);
  863. }
  864. }
  865. ///
  866. unittest {
  867. import std.conv;
  868.  
  869. // To be represented as the boxed value when serialized
  870. static struct Box(T) {
  871. T value;
  872. }
  873. // Also to berepresented as the boxed value when serialized, but has
  874. // a different way to access the value.
  875. static struct Box2(T) {
  876. private T v;
  877. ref T get() {
  878. return v;
  879. }
  880. }
  881. template BoxPol(S)
  882. {
  883. auto toRepresentation(S s) {
  884. return s.value;
  885. }
  886.  
  887. S fromRepresentation(typeof(toRepresentation(S.init)) v) {
  888. return S(v);
  889. }
  890. }
  891. template Box2Pol(S)
  892. {
  893. auto toRepresentation(S s) {
  894. return s.get();
  895. }
  896.  
  897. S fromRepresentation(typeof(toRepresentation(S.init)) v) {
  898. S s;
  899. s.get() = v;
  900. return s;
  901. }
  902. }
  903. alias ChainPol = ChainedPolicy!(BoxPol, Box2Pol);
  904. static assert(!isPolicySerializable!(BoxPol, Box2!int));
  905. static assert(!isPolicySerializable!(Box2Pol, Box!int));
  906. static assert(isPolicySerializable!(ChainPol, Box!int));
  907. static assert(isPolicySerializable!(ChainPol, Box2!int));
  908. }
  909.  
  910. private template ChainedPolicyImpl(alias Primary, alias Fallback)
  911. {
  912. template Pol(T)
  913. {
  914. static if (isPolicySerializable!(Primary, T)) {
  915. alias toRepresentation = Primary!T.toRepresentation;
  916. alias fromRepresentation = Primary!T.fromRepresentation;
  917. } else {
  918. alias toRepresentation = Fallback!T.toRepresentation;
  919. alias fromRepresentation = Fallback!T.fromRepresentation;
  920. }
  921. }
  922. alias ChainedPolicyImpl = Pol;
  923. }
  924.  
  925. private template hasAttribute(T, alias decl) { enum hasAttribute = findFirstUDA!(T, decl).found; }
  926.  
  927. unittest {
  928. @asArray int i1;
  929. static assert(hasAttribute!(AsArrayAttribute, i1));
  930. int i2;
  931. static assert(!hasAttribute!(AsArrayAttribute, i2));
  932. }
  933.  
  934. private template hasAttributeL(T, ATTRIBUTES...) {
  935. static if (ATTRIBUTES.length == 1) {
  936. enum hasAttributeL = is(typeof(ATTRIBUTES[0]) == T);
  937. } else static if (ATTRIBUTES.length > 1) {
  938. enum hasAttributeL = hasAttributeL!(T, ATTRIBUTES[0 .. $/2]) || hasAttributeL!(T, ATTRIBUTES[$/2 .. $]);
  939. } else {
  940. enum hasAttributeL = false;
  941. }
  942. }
  943.  
  944. unittest {
  945. static assert(hasAttributeL!(AsArrayAttribute, byName, asArray));
  946. static assert(!hasAttributeL!(AsArrayAttribute, byName));
  947. }
  948.  
  949. private static T getAttribute(TT, string mname, T)(T default_value)
  950. {
  951. enum val = findFirstUDA!(T, __traits(getMember, TT, mname));
  952. static if (val.found) return val.value;
  953. else return default_value;
  954. }
  955.  
  956. private string underscoreStrip(string field_name)
  957. {
  958. if( field_name.length < 1 || field_name[$-1] != '_' ) return field_name;
  959. else return field_name[0 .. $-1];
  960. }
  961.  
  962.  
  963. private template hasSerializableFields(T, size_t idx = 0)
  964. {
  965. enum hasSerializableFields = SerializableFields!(T).length > 0;
  966. /*static if (idx < __traits(allMembers, T).length) {
  967. enum mname = __traits(allMembers, T)[idx];
  968. static if (!isRWPlainField!(T, mname) && !isRWField!(T, mname)) enum hasSerializableFields = hasSerializableFields!(T, idx+1);
  969. else static if (hasAttribute!(IgnoreAttribute, __traits(getMember, T, mname))) enum hasSerializableFields = hasSerializableFields!(T, idx+1);
  970. else enum hasSerializableFields = true;
  971. } else enum hasSerializableFields = false;*/
  972. }
  973.  
  974. private template SerializableFields(COMPOSITE)
  975. {
  976. alias SerializableFields = FilterSerializableFields!(COMPOSITE, __traits(allMembers, COMPOSITE));
  977. }
  978.  
  979. private template FilterSerializableFields(COMPOSITE, FIELDS...)
  980. {
  981. static if (FIELDS.length > 1) {
  982. alias FilterSerializableFields = TypeTuple!(
  983. FilterSerializableFields!(COMPOSITE, FIELDS[0 .. $/2]),
  984. FilterSerializableFields!(COMPOSITE, FIELDS[$/2 .. $]));
  985. } else static if (FIELDS.length == 1) {
  986. alias T = COMPOSITE;
  987. enum mname = FIELDS[0];
  988. static if (isRWPlainField!(T, mname) || isRWField!(T, mname)) {
  989. alias Tup = TypeTuple!(__traits(getMember, COMPOSITE, FIELDS[0]));
  990. static if (Tup.length != 1) {
  991. alias FilterSerializableFields = TypeTuple!(mname);
  992. } else {
  993. static if (!hasAttribute!(IgnoreAttribute, __traits(getMember, T, mname)))
  994. alias FilterSerializableFields = TypeTuple!(mname);
  995. else alias FilterSerializableFields = TypeTuple!();
  996. }
  997. } else alias FilterSerializableFields = TypeTuple!();
  998. } else alias FilterSerializableFields = TypeTuple!();
  999. }
  1000.  
  1001. private size_t getExpandedFieldCount(T, FIELDS...)()
  1002. {
  1003. size_t ret = 0;
  1004. foreach (F; FIELDS) ret += TypeTuple!(__traits(getMember, T, F)).length;
  1005. return ret;
  1006. }
  1007.  
  1008. /******************************************************************************/
  1009. /* General serialization unit testing */
  1010. /******************************************************************************/
  1011.  
  1012. version (unittest) {
  1013. private struct TestSerializer {
  1014. import std.array, std.conv, std.string;
  1015.  
  1016. string result;
  1017.  
  1018. enum isSupportedValueType(T) = is(T == string) || is(T == typeof(null)) || is(T == float) || is (T == int);
  1019.  
  1020. string getSerializedResult() { return result; }
  1021. void beginWriteDictionary(T)() { result ~= "D("~T.mangleof~"){"; }
  1022. void endWriteDictionary(T)() { result ~= "}D("~T.mangleof~")"; }
  1023. void beginWriteDictionaryEntry(T)(string name) { result ~= "DE("~T.mangleof~","~name~")("; }
  1024. void endWriteDictionaryEntry(T)(string name) { result ~= ")DE("~T.mangleof~","~name~")"; }
  1025. void beginWriteArray(T)(size_t length) { result ~= "A("~T.mangleof~")["~length.to!string~"]["; }
  1026. void endWriteArray(T)() { result ~= "]A("~T.mangleof~")"; }
  1027. void beginWriteArrayEntry(T)(size_t i) { result ~= "AE("~T.mangleof~","~i.to!string~")("; }
  1028. void endWriteArrayEntry(T)(size_t i) { result ~= ")AE("~T.mangleof~","~i.to!string~")"; }
  1029. void writeValue(T)(T value) {
  1030. if (is(T == typeof(null))) result ~= "null";
  1031. else {
  1032. assert(isSupportedValueType!T);
  1033. result ~= "V("~T.mangleof~")("~value.to!string~")";
  1034. }
  1035. }
  1036.  
  1037. // deserialization
  1038. void readDictionary(T)(scope void delegate(string) entry_callback)
  1039. {
  1040. skip("D("~T.mangleof~"){");
  1041. while (result.startsWith("DE(")) {
  1042. result = result[3 .. $];
  1043. auto idx = result.indexOf(',');
  1044. auto idx2 = result.indexOf(")(");
  1045. assert(idx > 0 && idx2 > idx);
  1046. auto t = result[0 .. idx];
  1047. auto n = result[idx+1 .. idx2];
  1048. result = result[idx2+2 .. $];
  1049. entry_callback(n);
  1050. skip(")DE("~t~","~n~")");
  1051. }
  1052. skip("}D("~T.mangleof~")");
  1053. }
  1054.  
  1055. void readArray(T)(scope void delegate(size_t) size_callback, scope void delegate() entry_callback)
  1056. {
  1057. skip("A("~T.mangleof~")[");
  1058. auto bidx = result.indexOf("][");
  1059. assert(bidx > 0);
  1060. auto cnt = result[0 .. bidx].to!size_t;
  1061. result = result[bidx+2 .. $];
  1062.  
  1063. size_t i = 0;
  1064. while (result.startsWith("AE(")) {
  1065. result = result[3 .. $];
  1066. auto idx = result.indexOf(',');
  1067. auto idx2 = result.indexOf(")(");
  1068. assert(idx > 0 && idx2 > idx);
  1069. auto t = result[0 .. idx];
  1070. auto n = result[idx+1 .. idx2];
  1071. result = result[idx2+2 .. $];
  1072. assert(n == i.to!string);
  1073. entry_callback();
  1074. skip(")AE("~t~","~n~")");
  1075. i++;
  1076. }
  1077. skip("]A("~T.mangleof~")");
  1078.  
  1079. assert(i == cnt);
  1080. }
  1081.  
  1082. T readValue(T)()
  1083. {
  1084. skip("V("~T.mangleof~")(");
  1085. auto idx = result.indexOf(')');
  1086. assert(idx >= 0);
  1087. auto ret = result[0 .. idx].to!T;
  1088. result = result[idx+1 .. $];
  1089. return ret;
  1090. }
  1091.  
  1092. void skip(string prefix)
  1093. {
  1094. assert(result.startsWith(prefix), result);
  1095. result = result[prefix.length .. $];
  1096. }
  1097.  
  1098. bool tryReadNull()
  1099. {
  1100. if (result.startsWith("null")) {
  1101. result = result[4 .. $];
  1102. return true;
  1103. } else return false;
  1104. }
  1105. }
  1106. }
  1107.  
  1108. unittest { // basic serialization behavior
  1109. import std.typecons : Nullable;
  1110.  
  1111. static void test(T)(T value, string expected) {
  1112. assert(serialize!TestSerializer(value) == expected, serialize!TestSerializer(value));
  1113. static if (isPointer!T) {
  1114. if (value) assert(*deserialize!(TestSerializer, T)(expected) == *value);
  1115. else assert(deserialize!(TestSerializer, T)(expected) is null);
  1116. } else static if (is(T == Nullable!U, U)) {
  1117. if (value.isNull()) assert(deserialize!(TestSerializer, T)(expected).isNull);
  1118. else assert(deserialize!(TestSerializer, T)(expected) == value);
  1119. } else assert(deserialize!(TestSerializer, T)(expected) == value);
  1120. }
  1121.  
  1122. test("hello", "V(Aya)(hello)");
  1123. test(12, "V(i)(12)");
  1124. test(12.0, "V(Aya)(12)");
  1125. test(12.0f, "V(f)(12)");
  1126. assert(serialize!TestSerializer(null) == "null");
  1127. 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)");
  1128. string mangleOfAA = (string[string]).mangleof;
  1129. test(["hello": "world"], "D(" ~ mangleOfAA ~ "){DE(Aya,hello)(V(Aya)(world))DE(Aya,hello)}D(" ~ mangleOfAA ~ ")");
  1130. test(cast(int*)null, "null");
  1131. int i = 42;
  1132. test(&i, "V(i)(42)");
  1133. Nullable!int j;
  1134. test(j, "null");
  1135. j = 42;
  1136. test(j, "V(i)(42)");
  1137. }
  1138.  
  1139. unittest { // basic user defined types
  1140. static struct S { string f; }
  1141. enum Sm = S.mangleof;
  1142. auto s = S("hello");
  1143. enum s_ser = "D("~Sm~"){DE(Aya,f)(V(Aya)(hello))DE(Aya,f)}D("~Sm~")";
  1144. assert(serialize!TestSerializer(s) == s_ser, serialize!TestSerializer(s));
  1145. assert(deserialize!(TestSerializer, S)(s_ser) == s);
  1146.  
  1147. static class C { string f; }
  1148. enum Cm = C.mangleof;
  1149. C c;
  1150. assert(serialize!TestSerializer(c) == "null");
  1151. c = new C;
  1152. c.f = "hello";
  1153. enum c_ser = "D("~Cm~"){DE(Aya,f)(V(Aya)(hello))DE(Aya,f)}D("~Cm~")";
  1154. assert(serialize!TestSerializer(c) == c_ser);
  1155. assert(deserialize!(TestSerializer, C)(c_ser).f == c.f);
  1156.  
  1157. enum E { hello, world }
  1158. assert(serialize!TestSerializer(E.hello) == "V(i)(0)");
  1159. assert(serialize!TestSerializer(E.world) == "V(i)(1)");
  1160. }
  1161.  
  1162. unittest { // tuple serialization
  1163. import std.typecons : Tuple;
  1164.  
  1165. static struct S(T...) { T f; }
  1166. enum Sm = S!(int, string).mangleof;
  1167. enum Tum = Tuple!(int, string).mangleof;
  1168. auto s = S!(int, string)(42, "hello");
  1169. assert(serialize!TestSerializer(s) ==
  1170. "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~")");
  1171.  
  1172. static struct T { @asArray S!(int, string) g; }
  1173. enum Tm = T.mangleof;
  1174. auto t = T(s);
  1175. assert(serialize!TestSerializer(t) ==
  1176. "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~")");
  1177. }
  1178.  
  1179. unittest { // testing the various UDAs
  1180. enum E { hello, world }
  1181. enum Em = E.mangleof;
  1182. static struct S {
  1183. @byName E e;
  1184. @ignore int i;
  1185. @optional float f;
  1186. }
  1187. enum Sm = S.mangleof;
  1188. auto s = S(E.world, 42, 1.0f);
  1189. assert(serialize!TestSerializer(s) ==
  1190. "D("~Sm~"){DE("~Em~",e)(V(Aya)(world))DE("~Em~",e)DE(f,f)(V(f)(1))DE(f,f)}D("~Sm~")");
  1191. }
  1192.  
  1193. unittest { // custom serialization support
  1194. // iso-ext
  1195. import std.datetime;
  1196. auto t = TimeOfDay(6, 31, 23);
  1197. assert(serialize!TestSerializer(t) == "V(Aya)(06:31:23)");
  1198. auto d = Date(1964, 1, 23);
  1199. assert(serialize!TestSerializer(d) == "V(Aya)(1964-01-23)");
  1200. auto dt = DateTime(d, t);
  1201. assert(serialize!TestSerializer(dt) == "V(Aya)(1964-01-23T06:31:23)");
  1202. auto st = SysTime(dt, UTC());
  1203. assert(serialize!TestSerializer(st) == "V(Aya)(1964-01-23T06:31:23Z)");
  1204.  
  1205. // string
  1206. struct S1 { int i; string toString() const { return "hello"; } static S1 fromString(string) { return S1.init; } }
  1207. struct S2 { int i; string toString() const { return "hello"; } }
  1208. enum S2m = S2.mangleof;
  1209. struct S3 { int i; static S3 fromString(string) { return S3.init; } }
  1210. enum S3m = S3.mangleof;
  1211. assert(serialize!TestSerializer(S1.init) == "V(Aya)(hello)");
  1212. assert(serialize!TestSerializer(S2.init) == "D("~S2m~"){DE(i,i)(V(i)(0))DE(i,i)}D("~S2m~")");
  1213. assert(serialize!TestSerializer(S3.init) == "D("~S3m~"){DE(i,i)(V(i)(0))DE(i,i)}D("~S3m~")");
  1214.  
  1215. // custom
  1216. struct C1 { int i; float toRepresentation() const { return 1.0f; } static C1 fromRepresentation(float f) { return C1.init; } }
  1217. struct C2 { int i; float toRepresentation() const { return 1.0f; } }
  1218. enum C2m = C2.mangleof;
  1219. struct C3 { int i; static C3 fromRepresentation(float f) { return C3.init; } }
  1220. enum C3m = C3.mangleof;
  1221. assert(serialize!TestSerializer(C1.init) == "V(f)(1)");
  1222. assert(serialize!TestSerializer(C2.init) == "D("~C2m~"){DE(i,i)(V(i)(0))DE(i,i)}D("~C2m~")");
  1223. assert(serialize!TestSerializer(C3.init) == "D("~C3m~"){DE(i,i)(V(i)(0))DE(i,i)}D("~C3m~")");
  1224. }
  1225.  
  1226. unittest // Testing corner case: member function returning by ref
  1227. {
  1228. import dub.internal.vibecompat.data.json;
  1229.  
  1230. static struct S
  1231. {
  1232. int i;
  1233. ref int foo() { return i; }
  1234. }
  1235.  
  1236. static assert(__traits(compiles, { S().serializeToJson(); }));
  1237. static assert(__traits(compiles, { Json().deserializeJson!S(); }));
  1238.  
  1239. auto s = S(1);
  1240. assert(s.serializeToJson().deserializeJson!S() == s);
  1241. }
  1242.  
  1243. unittest // Testing corner case: Variadic template constructors and methods
  1244. {
  1245. import dub.internal.vibecompat.data.json;
  1246.  
  1247. static struct S
  1248. {
  1249. int i;
  1250. this(Args...)(Args args) {}
  1251. int foo(Args...)(Args args) { return i; }
  1252. ref int bar(Args...)(Args args) { return i; }
  1253. }
  1254.  
  1255. static assert(__traits(compiles, { S().serializeToJson(); }));
  1256. static assert(__traits(compiles, { Json().deserializeJson!S(); }));
  1257.  
  1258. auto s = S(1);
  1259. assert(s.serializeToJson().deserializeJson!S() == s);
  1260. }
  1261.  
  1262. unittest // Make sure serializing through properties still works
  1263. {
  1264. import dub.internal.vibecompat.data.json;
  1265.  
  1266. static struct S
  1267. {
  1268. public int i;
  1269. private int privateJ;
  1270.  
  1271. @property int j() { return privateJ; }
  1272. @property void j(int j) { privateJ = j; }
  1273. }
  1274.  
  1275. auto s = S(1, 2);
  1276. assert(s.serializeToJson().deserializeJson!S() == s);
  1277. }
  1278.  
  1279. unittest { // test BitFlags serialization
  1280. import std.typecons : BitFlags;
  1281.  
  1282. enum Flag {
  1283. a = 1<<0,
  1284. b = 1<<1,
  1285. c = 1<<2
  1286. }
  1287. enum Flagm = Flag.mangleof;
  1288.  
  1289. alias Flags = BitFlags!Flag;
  1290. enum Flagsm = Flags.mangleof;
  1291.  
  1292. enum Fi_ser = "A(A"~Flagm~")[0][]A(A"~Flagm~")";
  1293. assert(serialize!TestSerializer(Flags.init) == Fi_ser);
  1294.  
  1295. 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~")";
  1296. assert(serialize!TestSerializer(Flags(Flag.a, Flag.c)) == Fac_ser);
  1297.  
  1298. struct S { @byName Flags f; }
  1299. enum Sm = S.mangleof;
  1300. 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~")";
  1301.  
  1302. assert(serialize!TestSerializer(S(Flags(Flag.a, Flag.c))) == Sac_ser);
  1303.  
  1304. assert(deserialize!(TestSerializer, Flags)(Fi_ser) == Flags.init);
  1305. assert(deserialize!(TestSerializer, Flags)(Fac_ser) == Flags(Flag.a, Flag.c));
  1306. assert(deserialize!(TestSerializer, S)(Sac_ser) == S(Flags(Flag.a, Flag.c)));
  1307. }