Serialization.cs 45 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242
  1. using InABox.Clients;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Diagnostics.CodeAnalysis;
  5. using System.IO;
  6. using System.Linq;
  7. using System.Runtime.CompilerServices;
  8. using System.Text.Json;
  9. using System.Text.Json.Serialization;
  10. using System.Text.Json.Serialization.Metadata;
  11. namespace InABox.Core
  12. {
  13. public enum SerializationFormat
  14. {
  15. Json,
  16. Binary
  17. }
  18. public class SerialisationException : Exception
  19. {
  20. public SerialisationException(string message) : base(message) { }
  21. }
  22. public interface ISerializeBinary
  23. {
  24. public void SerializeBinary(CoreBinaryWriter writer);
  25. public void DeserializeBinary(CoreBinaryReader reader);
  26. }
  27. public static class Serialization
  28. {
  29. /// <summary>
  30. /// TypeInfoResolver modifier that removes properties that don't have setters.
  31. /// </summary>
  32. /// <param name="typeInfo"></param>
  33. public static void WritablePropertiesOnly(JsonTypeInfo typeInfo)
  34. {
  35. if (typeInfo.Kind == JsonTypeInfoKind.Object)
  36. {
  37. var toRemove = typeInfo.Properties.Where(x => x.Set is null).ToList();
  38. foreach (var prop in toRemove)
  39. {
  40. typeInfo.Properties.Remove(prop);
  41. }
  42. }
  43. }
  44. /// <summary>
  45. /// Remove properties marked as <see cref="DoNotSerialize"/>
  46. /// </summary>
  47. /// <param name="typeInfo"></param>
  48. private static void DoNotSerializeModifier(JsonTypeInfo typeInfo)
  49. {
  50. if (typeInfo.Kind == JsonTypeInfoKind.Object)
  51. {
  52. var toRemove = typeInfo.Properties.Where(x => x.AttributeProvider?.IsDefined(typeof(DoNotSerialize), false) == true).ToList();
  53. foreach (var prop in toRemove)
  54. {
  55. typeInfo.Properties.Remove(prop);
  56. }
  57. }
  58. }
  59. public static List<JsonConverter> DefaultConverters { get; } = new List<JsonConverter>()
  60. {
  61. new CoreTableJsonConverter(),
  62. new FilterJsonConverter(),
  63. new ColumnJsonConverter(),
  64. new ColumnsJsonConverter(),
  65. new SortOrderJsonConverter(),
  66. new MultiQueryRequestConverter(),
  67. new UserPropertiesJsonConverter(),
  68. new ValueTupleJsonConverter(),
  69. new TypeJsonConverter(),
  70. new PolymorphicConverter(),
  71. new ObjectConverter(), // Our fallback, which converts JSON objects into real ones.
  72. };
  73. private static JsonSerializerOptions SerializerSettings(bool indented = true, bool populateObject = false)
  74. {
  75. return CreateSerializerSettings(indented, populateObject);
  76. }
  77. public static JsonSerializerOptions CreateSerializerSettings(bool indented = true, bool populateObject = false)
  78. {
  79. var settings = new JsonSerializerOptions { };
  80. foreach (var converter in DefaultConverters)
  81. {
  82. settings.Converters.Add(converter);
  83. }
  84. if (populateObject)
  85. {
  86. settings.TypeInfoResolver = new PopulateTypeInfoResolver(new DefaultJsonTypeInfoResolver());
  87. }
  88. else
  89. {
  90. settings.TypeInfoResolver = new DefaultJsonTypeInfoResolver();
  91. }
  92. settings.TypeInfoResolver = settings.TypeInfoResolver
  93. .WithAddedModifier(DoNotSerializeModifier);
  94. settings.WriteIndented = indented;
  95. return settings;
  96. }
  97. public static string Serialize(object? o, bool indented = false)
  98. {
  99. var json = JsonSerializer.Serialize(o, SerializerSettings(indented));
  100. return json;
  101. }
  102. public static void Serialize(object o, Stream stream, bool indented = false)
  103. {
  104. var settings = SerializerSettings(indented);
  105. JsonSerializer.Serialize(stream, o, settings);
  106. }
  107. [return: MaybeNull]
  108. public static T Deserialize<T>(Stream? stream, bool strict = false)
  109. {
  110. if (stream == null)
  111. return default;
  112. try
  113. {
  114. var settings = SerializerSettings();
  115. return JsonSerializer.Deserialize<T>(stream, settings);
  116. }
  117. catch (Exception e)
  118. {
  119. if (strict)
  120. throw;
  121. Logger.Send(LogType.Error, ClientFactory.UserID, $"Error in Deserialize<{typeof(T)}>(): {e.Message}");
  122. return default;
  123. }
  124. }
  125. public static object? Deserialize(Type type, Stream? stream)
  126. {
  127. if (stream == null)
  128. return null;
  129. object? result = null;
  130. var settings = SerializerSettings();
  131. result = JsonSerializer.Deserialize(stream, type, settings);
  132. return result;
  133. }
  134. [return: MaybeNull]
  135. public static T Deserialize<T>(string? json, bool strict = false) // where T : new()
  136. {
  137. var ret = default(T);
  138. if (string.IsNullOrWhiteSpace(json))
  139. return ret;
  140. try
  141. {
  142. var settings = SerializerSettings();
  143. if (typeof(T).IsArray)
  144. {
  145. ret = JsonSerializer.Deserialize<T>(json, settings);
  146. }
  147. else
  148. {
  149. ret = JsonSerializer.Deserialize<T>(json, settings);
  150. }
  151. }
  152. catch (Exception e)
  153. {
  154. if (strict)
  155. {
  156. throw;
  157. }
  158. CoreUtils.LogException("", e);
  159. if (typeof(T).IsArray)
  160. {
  161. ret = (T)(object)Array.CreateInstance(typeof(T).GetElementType(), 0);
  162. }
  163. else
  164. {
  165. ret = (T)Activator.CreateInstance(typeof(T), true);
  166. }
  167. }
  168. return ret;
  169. }
  170. [return: MaybeNull]
  171. public static void DeserializeInto<T>(string? json, T obj, bool strict = false)
  172. {
  173. if (string.IsNullOrWhiteSpace(json))
  174. return;
  175. try
  176. {
  177. var settings = SerializerSettings(populateObject: true);
  178. PopulateTypeInfoResolver.t_populateObject = obj;
  179. if (typeof(T).IsArray)
  180. {
  181. JsonSerializer.Deserialize<T>(json, settings);
  182. }
  183. else
  184. {
  185. JsonSerializer.Deserialize<T>(json, settings);
  186. }
  187. }
  188. catch (Exception e)
  189. {
  190. if (strict)
  191. {
  192. throw;
  193. }
  194. CoreUtils.LogException("", e);
  195. }
  196. finally
  197. {
  198. PopulateTypeInfoResolver.t_populateObject = null;
  199. }
  200. }
  201. public static object? Deserialize(Type T, string json) // where T : new()
  202. {
  203. var ret = T.GetDefault();
  204. if (string.IsNullOrWhiteSpace(json))
  205. return ret;
  206. try
  207. {
  208. var settings = SerializerSettings();
  209. if (T.IsArray)
  210. {
  211. object o = Array.CreateInstance(T.GetElementType(), 0);
  212. ret = o;
  213. }
  214. else
  215. {
  216. ret = JsonSerializer.Deserialize(json, T, settings);
  217. }
  218. }
  219. catch (Exception)
  220. {
  221. ret = Activator.CreateInstance(T, true);
  222. }
  223. return ret;
  224. }
  225. #region Binary Serialization
  226. public static byte[] WriteBinary(this ISerializeBinary obj, BinarySerializationSettings settings)
  227. {
  228. using var stream = new MemoryStream();
  229. obj.SerializeBinary(new CoreBinaryWriter(stream, settings));
  230. return stream.ToArray();
  231. }
  232. public static void WriteBinary(this ISerializeBinary obj, Stream stream, BinarySerializationSettings settings)
  233. {
  234. obj.SerializeBinary(new CoreBinaryWriter(stream, settings));
  235. }
  236. public static T ReadBinary<T>(byte[] data, BinarySerializationSettings settings)
  237. where T : ISerializeBinary, new() => (T)ReadBinary(typeof(T), data, settings);
  238. public static T ReadBinary<T>(Stream stream, BinarySerializationSettings settings)
  239. where T : ISerializeBinary, new() => (T)ReadBinary(typeof(T), stream, settings);
  240. public static object ReadBinary(Type T, byte[] data, BinarySerializationSettings settings)
  241. {
  242. using var stream = new MemoryStream(data);
  243. return ReadBinary(T, stream, settings);
  244. }
  245. public static object ReadBinary(Type T, Stream stream, BinarySerializationSettings settings)
  246. {
  247. var obj = (Activator.CreateInstance(T) as ISerializeBinary)!;
  248. obj.DeserializeBinary(new CoreBinaryReader(stream, settings));
  249. return obj;
  250. }
  251. #endregion
  252. }
  253. internal class PopulateTypeInfoResolver : IJsonTypeInfoResolver
  254. {
  255. private readonly IJsonTypeInfoResolver? _jsonTypeInfoResolver;
  256. [ThreadStatic]
  257. internal static object? t_populateObject;
  258. public PopulateTypeInfoResolver(IJsonTypeInfoResolver? jsonTypeInfoResolver)
  259. {
  260. _jsonTypeInfoResolver = jsonTypeInfoResolver;
  261. }
  262. public JsonTypeInfo? GetTypeInfo(Type type, JsonSerializerOptions options)
  263. {
  264. var typeInfo = _jsonTypeInfoResolver?.GetTypeInfo(type, options);
  265. if (typeInfo != null && typeInfo.Kind != JsonTypeInfoKind.None)
  266. {
  267. var defaultCreateObject = typeInfo.CreateObject;
  268. if (defaultCreateObject != null)
  269. {
  270. typeInfo.CreateObject = () =>
  271. {
  272. if (t_populateObject != null)
  273. {
  274. var result = t_populateObject;
  275. t_populateObject = null;
  276. return result;
  277. }
  278. else
  279. {
  280. return defaultCreateObject.Invoke();
  281. }
  282. };
  283. }
  284. }
  285. return typeInfo;
  286. }
  287. }
  288. public class CoreBinaryReader : BinaryReader
  289. {
  290. public BinarySerializationSettings Settings { get; set; }
  291. public CoreBinaryReader(Stream stream, BinarySerializationSettings settings) : base(stream)
  292. {
  293. Settings = settings;
  294. }
  295. }
  296. public class CoreBinaryWriter : BinaryWriter
  297. {
  298. public BinarySerializationSettings Settings { get; set; }
  299. public CoreBinaryWriter(Stream stream, BinarySerializationSettings settings) : base(stream)
  300. {
  301. Settings = settings;
  302. }
  303. }
  304. /// <summary>
  305. /// A class to maintain the consistency of serialisation formats across versions.
  306. /// The design of this is such that specific versions of serialisation have different parameters set,
  307. /// and the versions are maintained as static properties. Please keep the constructor private.
  308. /// </summary>
  309. /// <remarks>
  310. /// Note that <see cref="Latest"/> should always be updated to point to the latest version.
  311. /// <br/>
  312. /// Note also that all versions should have an entry in the <see cref="ConvertVersionString(string)"/> function.
  313. /// <br/>
  314. /// Also, if you create a new format, it would probably be a good idea to add a database update script to get all
  315. /// <see cref="IPackable"/> and <see cref="ISerializeBinary"/> properties and update the version of the format.
  316. /// (Otherwise, we'd basically be nullifying all data that is currently binary serialised.)
  317. /// </remarks>
  318. public class BinarySerializationSettings
  319. {
  320. /// <summary>
  321. /// Should the Info() call return RPC and Rest Ports? This is
  322. /// To workaround a bug in RPCsockets that crash on large uploads
  323. /// </summary>
  324. /// <remarks>
  325. /// True in all serialization versions >= 1.2
  326. /// </remarks>
  327. public bool RPCClientWorkaround { get; set; }
  328. /// <summary>
  329. /// Should reference types include a flag for nullability? (Adds an extra boolean field for whether the value is null or not).
  330. /// </summary>
  331. /// <remarks>
  332. /// True in all serialisation versions >= 1.1.
  333. /// </remarks>
  334. public bool IncludeNullables { get; set; }
  335. public string Version { get; set; }
  336. public static BinarySerializationSettings Latest => V1_2;
  337. public static BinarySerializationSettings V1_0 = new BinarySerializationSettings("1.0")
  338. {
  339. IncludeNullables = false,
  340. RPCClientWorkaround = false
  341. };
  342. public static BinarySerializationSettings V1_1 = new BinarySerializationSettings("1.1")
  343. {
  344. IncludeNullables = true,
  345. RPCClientWorkaround = false
  346. };
  347. public static BinarySerializationSettings V1_2 = new BinarySerializationSettings("1.2")
  348. {
  349. IncludeNullables = true,
  350. RPCClientWorkaround = true
  351. };
  352. public static BinarySerializationSettings ConvertVersionString(string version) => version switch
  353. {
  354. "1.0" => V1_0,
  355. "1.1" => V1_1,
  356. "1.2" => V1_2,
  357. _ => V1_0
  358. };
  359. private BinarySerializationSettings(string version)
  360. {
  361. Version = version;
  362. }
  363. }
  364. public static class SerializationUtils
  365. {
  366. public static void Write(this BinaryWriter writer, Guid guid)
  367. {
  368. writer.Write(guid.ToByteArray());
  369. }
  370. public static Guid ReadGuid(this BinaryReader reader)
  371. {
  372. return new Guid(reader.ReadBytes(16));
  373. }
  374. public static void Write(this BinaryWriter writer, DateTime dateTime)
  375. {
  376. writer.Write(dateTime.Ticks);
  377. }
  378. public static DateTime ReadDateTime(this BinaryReader reader)
  379. {
  380. return new DateTime(reader.ReadInt64());
  381. }
  382. private static bool MatchType<T1>(Type t) => typeof(T1) == t;
  383. private static bool MatchType<T1, T2>(Type t) => (typeof(T1) == t) || (typeof(T2) == t);
  384. /// <summary>
  385. /// Binary serialize a bunch of different types of values. <see cref="WriteBinaryValue(CoreBinaryWriter, Type, object?)"/> and
  386. /// <see cref="ReadBinaryValue(CoreBinaryReader, Type)"/> are inverses of each other.
  387. /// </summary>
  388. /// <remarks>
  389. /// Handles <see cref="byte"/>[], <see cref="Array"/>s of serialisable values, <see cref="Enum"/>, <see cref="bool"/>, <see cref="string"/>,
  390. /// <see cref="Guid"/>, <see cref="byte"/>, <see cref="Int16"/>, <see cref="Int32"/>, <see cref="Int64"/>, <see cref="float"/>, <see cref="double"/>,
  391. /// <see cref="DateTime"/>, <see cref="TimeSpan"/>, <see cref="LoggablePropertyAttribute"/>, <see cref="IPackable"/>, <see cref="Nullable{T}"/>
  392. /// and <see cref="ISerializeBinary"/>.
  393. /// </remarks>
  394. /// <param name="writer"></param>
  395. /// <param name="type"></param>
  396. /// <param name="value"></param>
  397. /// <exception cref="Exception">If an object of <paramref name="type"/> is unable to be serialized.</exception>
  398. public static void WriteBinaryValue(this CoreBinaryWriter writer, Type type, object? value)
  399. {
  400. value ??= CoreUtils.GetDefault(type);
  401. if (value == null)
  402. {
  403. if (MatchType<string>(type))
  404. writer.Write("");
  405. else if (writer.Settings.IncludeNullables && typeof(IPackable).IsAssignableFrom(type))
  406. writer.Write(false);
  407. else if (writer.Settings.IncludeNullables && typeof(ISerializeBinary).IsAssignableFrom(type))
  408. writer.Write(false);
  409. else if (Nullable.GetUnderlyingType(type) is Type t)
  410. writer.Write(false);
  411. else if (MatchType<LoggablePropertyAttribute, object>(type))
  412. writer.Write("");
  413. else
  414. writer.Write(0);
  415. }
  416. else if (MatchType<byte[], object>(type) && value is byte[] bArray)
  417. {
  418. writer.Write(bArray.Length);
  419. writer.Write(bArray);
  420. }
  421. else if (type.IsArray && value is Array array)
  422. {
  423. var elementType = type.GetElementType();
  424. writer.Write(array.Length);
  425. foreach (var val1 in array)
  426. {
  427. WriteBinaryValue(writer, elementType, val1);
  428. }
  429. }
  430. else if (type.IsEnum && value is Enum e)
  431. {
  432. var underlyingType = type.GetEnumUnderlyingType();
  433. WriteBinaryValue(writer, underlyingType, Convert.ChangeType(e, underlyingType));
  434. }
  435. else if (MatchType<bool, object>(type) && value is bool b)
  436. {
  437. writer.Write(b);
  438. }
  439. else if (MatchType<string, object>(type) && value is string str)
  440. writer.Write(str);
  441. else if (MatchType<Guid, object>(type) && value is Guid guid)
  442. writer.Write(guid);
  443. else if (MatchType<byte, object>(type) && value is byte i8)
  444. writer.Write(i8);
  445. else if (MatchType<Int16, object>(type) && value is Int16 i16)
  446. writer.Write(i16);
  447. else if (MatchType<Int32, object>(type) && value is Int32 i32)
  448. writer.Write(i32);
  449. else if (MatchType<Int64, object>(type) && value is Int64 i64)
  450. writer.Write(i64);
  451. else if (MatchType<float, object>(type) && value is float f32)
  452. writer.Write(f32);
  453. else if (MatchType<double, object>(type) && value is double f64)
  454. writer.Write(f64);
  455. else if (MatchType<DateTime, object>(type) && value is DateTime date)
  456. writer.Write(date.Ticks);
  457. else if (MatchType<TimeSpan, object>(type) && value is TimeSpan time)
  458. writer.Write(time.Ticks);
  459. else if (MatchType<LoggablePropertyAttribute, object>(type) && value is LoggablePropertyAttribute lpa)
  460. writer.Write(lpa.Format ?? string.Empty);
  461. else if (typeof(IPackable).IsAssignableFrom(type) && value is IPackable pack)
  462. {
  463. if (writer.Settings.IncludeNullables)
  464. writer.Write(true);
  465. pack.Pack(writer);
  466. }
  467. else if (typeof(ISerializeBinary).IsAssignableFrom(type) && value is ISerializeBinary binary)
  468. {
  469. if (writer.Settings.IncludeNullables)
  470. writer.Write(true);
  471. binary.SerializeBinary(writer);
  472. }
  473. else if (Nullable.GetUnderlyingType(type) is Type t)
  474. {
  475. writer.Write(true);
  476. writer.WriteBinaryValue(t, value);
  477. }
  478. else if (value is UserProperty userprop)
  479. WriteBinaryValue(writer, userprop.Type, userprop.Value);
  480. else
  481. throw new SerialisationException($"Invalid type; Target DataType is {type} and value DataType is {value?.GetType().ToString() ?? "null"}");
  482. }
  483. public static void WriteBinaryValue<T>(this CoreBinaryWriter writer, T value)
  484. => WriteBinaryValue(writer, typeof(T), value);
  485. /// <summary>
  486. /// Binary deserialize a bunch of different types of values. <see cref="WriteBinaryValue(CoreBinaryWriter, Type, object?)"/> and
  487. /// <see cref="ReadBinaryValue(CoreBinaryReader, Type)"/> are inverses of each other.
  488. /// </summary>
  489. /// <remarks>
  490. /// Handles <see cref="byte"/>[], <see cref="Array"/>s of serialisable values, <see cref="Enum"/>, <see cref="bool"/>, <see cref="string"/>,
  491. /// <see cref="Guid"/>, <see cref="byte"/>, <see cref="Int16"/>, <see cref="Int32"/>, <see cref="Int64"/>, <see cref="float"/>, <see cref="double"/>,
  492. /// <see cref="DateTime"/>, <see cref="TimeSpan"/>, <see cref="LoggablePropertyAttribute"/>, <see cref="IPackable"/>, <see cref="Nullable{T}"/>
  493. /// and <see cref="ISerializeBinary"/>.
  494. /// </remarks>
  495. /// <param name="reader"></param>
  496. /// <param name="type"></param>
  497. /// <exception cref="Exception">If an object of <paramref name="type"/> is unable to be deserialized.</exception>
  498. public static object? ReadBinaryValue(this CoreBinaryReader reader, Type type)
  499. {
  500. if (type == typeof(byte[]))
  501. {
  502. var length = reader.ReadInt32();
  503. return reader.ReadBytes(length);
  504. }
  505. else if (type.IsArray)
  506. {
  507. var length = reader.ReadInt32();
  508. var elementType = type.GetElementType();
  509. var array = Array.CreateInstance(elementType, length);
  510. for (int i = 0; i < array.Length; ++i)
  511. {
  512. array.SetValue(ReadBinaryValue(reader, elementType), i);
  513. }
  514. return array;
  515. }
  516. else if (type.IsEnum)
  517. {
  518. var val = ReadBinaryValue(reader, type.GetEnumUnderlyingType());
  519. return Enum.ToObject(type, val);
  520. }
  521. else if (type == typeof(bool))
  522. {
  523. return reader.ReadBoolean();
  524. }
  525. else if (type == typeof(string))
  526. {
  527. return reader.ReadString();
  528. }
  529. else if (type == typeof(Guid))
  530. {
  531. return reader.ReadGuid();
  532. }
  533. else if (type == typeof(byte))
  534. {
  535. return reader.ReadByte();
  536. }
  537. else if (type == typeof(Int16))
  538. {
  539. return reader.ReadInt16();
  540. }
  541. else if (type == typeof(Int32))
  542. {
  543. return reader.ReadInt32();
  544. }
  545. else if (type == typeof(Int64))
  546. {
  547. return reader.ReadInt64();
  548. }
  549. else if (type == typeof(float))
  550. {
  551. return reader.ReadSingle();
  552. }
  553. else if (type == typeof(double))
  554. {
  555. return reader.ReadDouble();
  556. }
  557. else if (type == typeof(DateTime))
  558. {
  559. return new DateTime(reader.ReadInt64());
  560. }
  561. else if (type == typeof(TimeSpan))
  562. {
  563. return new TimeSpan(reader.ReadInt64());
  564. }
  565. else if (type == typeof(LoggablePropertyAttribute))
  566. {
  567. String format = reader.ReadString();
  568. return String.IsNullOrWhiteSpace(format)
  569. ? null
  570. : new LoggablePropertyAttribute() { Format = format };
  571. }
  572. else if (typeof(IPackable).IsAssignableFrom(type))
  573. {
  574. if (!reader.Settings.IncludeNullables || reader.ReadBoolean()) // Note the short-circuit operator preventing reading a boolean.
  575. {
  576. var packable = (Activator.CreateInstance(type) as IPackable)!;
  577. packable.Unpack(reader);
  578. return packable;
  579. }
  580. else
  581. {
  582. return null;
  583. }
  584. }
  585. else if (typeof(ISerializeBinary).IsAssignableFrom(type))
  586. {
  587. if (!reader.Settings.IncludeNullables || reader.ReadBoolean()) // Note the short-circuit operator preventing reading a boolean.
  588. {
  589. var obj = (Activator.CreateInstance(type, true) as ISerializeBinary)!;
  590. obj.DeserializeBinary(reader);
  591. return obj;
  592. }
  593. else
  594. {
  595. return null;
  596. }
  597. }
  598. else if (Nullable.GetUnderlyingType(type) is Type t)
  599. {
  600. var isNull = reader.ReadBoolean();
  601. if (isNull)
  602. {
  603. return null;
  604. }
  605. else
  606. {
  607. return reader.ReadBinaryValue(t);
  608. }
  609. }
  610. else
  611. {
  612. throw new SerialisationException($"Invalid type; Target DataType is {type}");
  613. }
  614. }
  615. public static T ReadBinaryValue<T>(this CoreBinaryReader reader)
  616. {
  617. var result = ReadBinaryValue(reader, typeof(T));
  618. return (result != null ? (T)result : default)!;
  619. }
  620. public static IEnumerable<IProperty> SerializableProperties(Type type, Predicate<IProperty>? filter = null) =>
  621. DatabaseSchema.Properties(type)
  622. .Where(x => (!(x is StandardProperty st) || st.IsSerializable) && (filter?.Invoke(x) ?? true));
  623. private static void WriteOriginalValues<TObject>(this CoreBinaryWriter writer, TObject obj)
  624. where TObject : BaseObject
  625. {
  626. var originalValues = new List<Tuple<Type, string, object?>>();
  627. foreach (var (key, value) in obj.OriginalValueList)
  628. {
  629. if (DatabaseSchema.Property(obj.GetType(), key) is IProperty prop && prop.IsSerializable)
  630. {
  631. originalValues.Add(new Tuple<Type, string, object?>(prop.PropertyType, key, value));
  632. }
  633. }
  634. writer.Write(originalValues.Count);
  635. foreach (var (type, key, value) in originalValues)
  636. {
  637. writer.Write(key);
  638. try
  639. {
  640. writer.WriteBinaryValue(type, value);
  641. }
  642. catch (Exception e)
  643. {
  644. CoreUtils.LogException("", e, "Error serialising OriginalValues");
  645. }
  646. }
  647. }
  648. private static void ReadOriginalValues<TObject>(this CoreBinaryReader reader, TObject obj)
  649. where TObject : BaseObject
  650. {
  651. var nOriginalValues = reader.ReadInt32();
  652. for (int i = 0; i < nOriginalValues; ++i)
  653. {
  654. var key = reader.ReadString();
  655. if (DatabaseSchema.Property(obj.GetType(), key) is IProperty prop)
  656. {
  657. var value = reader.ReadBinaryValue(prop.PropertyType);
  658. obj.OriginalValueList[prop.Name] = value;
  659. }
  660. }
  661. }
  662. public static void WriteObject<TObject>(this CoreBinaryWriter writer, TObject entity, Type type)
  663. where TObject : BaseObject
  664. {
  665. if (!typeof(TObject).IsAssignableFrom(type))
  666. throw new SerialisationException($"{type.EntityName()} is not a subclass of {typeof(TObject).EntityName()}");
  667. var properties = SerializableProperties(type).ToList();
  668. writer.Write(properties.Count);
  669. foreach (var property in properties)
  670. {
  671. writer.Write(property.Name);
  672. writer.WriteBinaryValue(property.PropertyType, property.Getter()(entity));
  673. }
  674. writer.WriteOriginalValues(entity);
  675. }
  676. /// <summary>
  677. /// An implementation of binary serialising a <typeparamref name="TObject"/>; this is the inverse of <see cref="ReadObject{TObject}(CoreBinaryReader)"/>.
  678. /// </summary>
  679. /// <remarks>
  680. /// Also serialises the names of properties along with the values.
  681. /// </remarks>
  682. /// <typeparam name="TObject"></typeparam>
  683. /// <param name="writer"></param>
  684. /// <param name="entity"></param>
  685. public static void WriteObject<TObject>(this CoreBinaryWriter writer, TObject entity)
  686. where TObject : BaseObject, new() => WriteObject(writer, entity, typeof(TObject));
  687. public static TObject ReadObject<TObject>(this CoreBinaryReader reader, Type type)
  688. where TObject : BaseObject
  689. {
  690. if (!typeof(TObject).IsAssignableFrom(type))
  691. throw new SerialisationException($"{type.EntityName()} is not a subclass of {typeof(TObject).EntityName()}");
  692. var obj = (Activator.CreateInstance(type) as TObject)!;
  693. obj.SetObserving(false);
  694. var nProps = reader.ReadInt32();
  695. for (int i = 0; i < nProps; ++i)
  696. {
  697. var propName = reader.ReadString();
  698. var property = DatabaseSchema.Property(type, propName)
  699. ?? throw new SerialisationException($"Property {propName} does not exist on {type.EntityName()}");
  700. property.Setter()(obj, reader.ReadBinaryValue(property.PropertyType));
  701. }
  702. reader.ReadOriginalValues(obj);
  703. obj.SetObserving(true);
  704. return obj;
  705. }
  706. /// <summary>
  707. /// The inverse of <see cref="WriteObject{TObject}(CoreBinaryWriter, TObject)"/>.
  708. /// </summary>
  709. /// <typeparam name="TObject"></typeparam>
  710. /// <param name="reader"></param>
  711. /// <returns></returns>
  712. public static TObject ReadObject<TObject>(this CoreBinaryReader reader)
  713. where TObject : BaseObject, new() => reader.ReadObject<TObject>(typeof(TObject));
  714. /// <summary>
  715. /// An implementation of binary serialising multiple <typeparamref name="TObject"/>s;
  716. /// this is the inverse of <see cref="ReadObjects{TObject}(CoreBinaryReader)"/>.
  717. /// </summary>
  718. /// <remarks>
  719. /// Also serialises the names of properties along with the values.
  720. /// </remarks>
  721. /// <typeparam name="TObject"></typeparam>
  722. /// <param name="writer"></param>
  723. /// <param name="objects"></param>
  724. public static void WriteObjects<TObject>(this CoreBinaryWriter writer, ICollection<TObject>? objects)
  725. where TObject : BaseObject, new() => WriteObjects(writer, typeof(TObject), objects);
  726. public static void WriteObjects<TObject>(this CoreBinaryWriter writer, Type type, ICollection<TObject>? objects, Predicate<IProperty>? filter = null)
  727. where TObject : BaseObject
  728. {
  729. if (!typeof(TObject).IsAssignableFrom(type))
  730. throw new SerialisationException($"{type.EntityName()} is not a subclass of {typeof(TObject).EntityName()}");
  731. var nObjs = objects?.Count ?? 0;
  732. writer.Write(nObjs);
  733. if (nObjs == 0)
  734. {
  735. return;
  736. }
  737. var properties = SerializableProperties(type, filter).ToList();
  738. writer.Write(properties.Count);
  739. foreach (var property in properties)
  740. {
  741. writer.Write(property.Name);
  742. }
  743. if (objects != null)
  744. {
  745. foreach (var obj in objects)
  746. {
  747. foreach (var property in properties)
  748. {
  749. writer.WriteBinaryValue(property.PropertyType, property.Getter()(obj));
  750. }
  751. writer.WriteOriginalValues(obj);
  752. }
  753. }
  754. }
  755. /// <summary>
  756. /// The inverse of <see cref="WriteObjects{TObject}(CoreBinaryWriter, ICollection{TObject})"/>.
  757. /// </summary>
  758. /// <typeparam name="TObject"></typeparam>
  759. /// <param name="reader"></param>
  760. /// <returns></returns>
  761. public static List<TObject> ReadObjects<TObject>(this CoreBinaryReader reader)
  762. where TObject : BaseObject, new()
  763. {
  764. return ReadObjects<TObject>(reader, typeof(TObject));
  765. }
  766. public static List<TObject> ReadObjects<TObject>(this CoreBinaryReader reader, Type type) where TObject : BaseObject
  767. {
  768. if (!typeof(TObject).IsAssignableFrom(type))
  769. throw new SerialisationException($"{type.EntityName()} is not a subclass of {typeof(TObject).EntityName()}");
  770. var objs = new List<TObject>();
  771. var properties = new List<IProperty>();
  772. var nObjs = reader.ReadInt32();
  773. if (nObjs == 0)
  774. {
  775. return objs;
  776. }
  777. var nProps = reader.ReadInt32();
  778. for (int i = 0; i < nProps; ++i)
  779. {
  780. var propertyName = reader.ReadString();
  781. var property = DatabaseSchema.Property(type, propertyName)
  782. ?? throw new SerialisationException($"Property {propertyName} does not exist on {type.EntityName()}");
  783. properties.Add(property);
  784. }
  785. for (int i = 0; i < nObjs; ++i)
  786. {
  787. var obj = (Activator.CreateInstance(type) as TObject)!;
  788. obj.SetObserving(false);
  789. foreach (var property in properties)
  790. {
  791. property.Setter()(obj, reader.ReadBinaryValue(property.PropertyType));
  792. }
  793. reader.ReadOriginalValues(obj);
  794. obj.SetObserving(true);
  795. objs.Add(obj);
  796. }
  797. return objs;
  798. }
  799. }
  800. /// <summary>
  801. /// When serialising an object implementing this interface, a '$type' field will be added.
  802. /// </summary>
  803. public interface IPolymorphicallySerialisable { }
  804. /// <summary>
  805. /// Adds a '$type' property to all classes that implement <see cref="IPolymorphicallySerialisable"/>.
  806. /// </summary>
  807. public class PolymorphicConverter : JsonConverter<object>
  808. {
  809. public override bool CanConvert(Type typeToConvert)
  810. {
  811. return typeof(IPolymorphicallySerialisable).IsAssignableFrom(typeToConvert) && (typeToConvert.IsInterface || typeToConvert.IsAbstract);
  812. }
  813. public override object? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
  814. {
  815. var dictionary = JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(ref reader, options);
  816. if (dictionary is null) return null;
  817. if(dictionary.TryGetValue("$type", out var typeName))
  818. {
  819. var type = Type.GetType(typeName.GetString() ?? "")!;
  820. dictionary.Remove("$type");
  821. var data = JsonSerializer.Serialize(dictionary, options);
  822. return JsonSerializer.Deserialize(data, type, options);
  823. }
  824. else
  825. {
  826. return null;
  827. }
  828. }
  829. public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options)
  830. {
  831. writer.WriteStartObject();
  832. writer.WriteString("$type", value.GetType().AssemblyQualifiedName);
  833. var internalSerialisation = JsonSerializer.Serialize(value, options)[1..^1];
  834. if (!internalSerialisation.IsNullOrWhiteSpace())
  835. {
  836. writer.WriteRawValue(internalSerialisation, true);
  837. }
  838. writer.WriteEndObject();
  839. }
  840. }
  841. public abstract class CustomJsonConverter<T> : JsonConverter<T>
  842. {
  843. protected object? ReadJson(ref Utf8JsonReader reader)
  844. {
  845. switch (reader.TokenType)
  846. {
  847. case JsonTokenType.String:
  848. return reader.GetString();
  849. case JsonTokenType.Number:
  850. if (reader.TryGetInt32(out int intValue))
  851. return intValue;
  852. if (reader.TryGetDouble(out double doubleValue))
  853. return doubleValue;
  854. return null;
  855. case JsonTokenType.True:
  856. return true;
  857. case JsonTokenType.False:
  858. return false;
  859. case JsonTokenType.Null:
  860. return null;
  861. case JsonTokenType.StartArray:
  862. var values = new List<object?>();
  863. reader.Read();
  864. while(reader.TokenType != JsonTokenType.EndArray)
  865. {
  866. values.Add(ReadJson(ref reader));
  867. reader.Read();
  868. }
  869. return values;
  870. default:
  871. return null;
  872. }
  873. }
  874. protected T ReadEnum<T>(ref Utf8JsonReader reader)
  875. where T : struct
  876. {
  877. if(reader.TokenType == JsonTokenType.Number)
  878. {
  879. return (T)Enum.ToObject(typeof(T), reader.GetInt32());
  880. }
  881. else
  882. {
  883. return Enum.Parse<T>(reader.GetString());
  884. }
  885. }
  886. protected delegate void ArrayValueHandler(ref Utf8JsonReader reader);
  887. protected void ReadArray(ref Utf8JsonReader reader, ArrayValueHandler onValue)
  888. {
  889. if (reader.TokenType != JsonTokenType.StartArray)
  890. {
  891. throw new JsonException();
  892. }
  893. while (reader.Read() && reader.TokenType != JsonTokenType.EndArray)
  894. {
  895. onValue(ref reader);
  896. }
  897. }
  898. protected delegate void ObjectPropertyHandler(ref Utf8JsonReader reader, string propertyName);
  899. protected void ReadObject(ref Utf8JsonReader reader, ObjectPropertyHandler onProperty)
  900. {
  901. if (reader.TokenType != JsonTokenType.StartObject)
  902. {
  903. throw new JsonException();
  904. }
  905. while (reader.Read() && reader.TokenType != JsonTokenType.EndObject)
  906. {
  907. if(reader.TokenType != JsonTokenType.PropertyName)
  908. {
  909. throw new JsonException();
  910. }
  911. var property = reader.GetString() ?? "";
  912. reader.Read();
  913. onProperty(ref reader, property);
  914. }
  915. }
  916. /// <summary>
  917. /// Write a value as a JSON object; note that some data types, like
  918. /// <see cref="Guid"/> and <see cref="DateTime"/> will be encoded as
  919. /// strings, and therefore will be returned as strings when read by
  920. /// <see cref="ReadJson(Utf8JsonReader)"/>. However, all types that
  921. /// this can write should be able to be retrieved by calling <see
  922. /// cref="CoreUtils.ChangeType(object?, Type)"/> on the resultant
  923. /// value.
  924. /// </summary>
  925. protected void WriteJson(Utf8JsonWriter writer, object? value)
  926. {
  927. if (value == null)
  928. writer.WriteNullValue();
  929. else if (value is string sVal)
  930. writer.WriteStringValue(sVal);
  931. else if (value is bool bVal)
  932. writer.WriteBooleanValue(bVal);
  933. else if (value is byte b)
  934. writer.WriteNumberValue(b);
  935. else if (value is short i16)
  936. writer.WriteNumberValue(i16);
  937. else if (value is int i32)
  938. writer.WriteNumberValue(i32);
  939. else if (value is long i64)
  940. writer.WriteNumberValue(i64);
  941. else if (value is float f)
  942. writer.WriteNumberValue(f);
  943. else if (value is double dVal)
  944. writer.WriteNumberValue(dVal);
  945. else if (value is DateTime dtVal)
  946. writer.WriteStringValue(dtVal.ToString());
  947. else if (value is TimeSpan tsVal)
  948. writer.WriteStringValue(tsVal.ToString());
  949. else if (value is Guid guid)
  950. writer.WriteStringValue(guid.ToString());
  951. else if(value is byte[] arr)
  952. {
  953. writer.WriteBase64StringValue(arr);
  954. }
  955. else if(value is Array array)
  956. {
  957. writer.WriteStartArray();
  958. foreach(var val1 in array)
  959. {
  960. WriteJson(writer, val1);
  961. }
  962. writer.WriteEndArray();
  963. }
  964. else if(value is Enum e)
  965. {
  966. WriteJson(writer, Convert.ChangeType(e, e.GetType().GetEnumUnderlyingType()));
  967. }
  968. else
  969. {
  970. Logger.Send(LogType.Error, "", $"Could not write object of type {value.GetType()} as JSON");
  971. }
  972. }
  973. protected void WriteJson(Utf8JsonWriter writer, string name, object? value)
  974. {
  975. writer.WritePropertyName(name);
  976. WriteJson(writer, value);
  977. }
  978. }
  979. public class ObjectConverter : CustomJsonConverter<object?>
  980. {
  981. public override object? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
  982. {
  983. switch (reader.TokenType)
  984. {
  985. case JsonTokenType.StartObject:
  986. var dict = new Dictionary<string, object?>();
  987. ReadObject(ref reader, (ref Utf8JsonReader reader, string property) =>
  988. {
  989. dict[property] = Read(ref reader, typeof(object), options);
  990. });
  991. return dict;
  992. case JsonTokenType.StartArray:
  993. var list = new List<object?>();
  994. ReadArray(ref reader, (ref Utf8JsonReader reader) =>
  995. {
  996. list.Add(Read(ref reader, typeof(object), options));
  997. });
  998. return list.ToArray();
  999. case JsonTokenType.String:
  1000. return reader.GetString();
  1001. case JsonTokenType.False:
  1002. return false;
  1003. case JsonTokenType.True:
  1004. return true;
  1005. case JsonTokenType.Number:
  1006. if(reader.TryGetInt32(out var iValue))
  1007. {
  1008. return iValue;
  1009. }
  1010. else if(reader.TryGetInt64(out var lValue))
  1011. {
  1012. return lValue;
  1013. }
  1014. else if(reader.TryGetDouble(out var dValue))
  1015. {
  1016. return dValue;
  1017. }
  1018. else
  1019. {
  1020. return null;
  1021. }
  1022. case JsonTokenType.Null:
  1023. return null;
  1024. default:
  1025. throw new JsonException();
  1026. }
  1027. }
  1028. public override void Write(Utf8JsonWriter writer, object? value, JsonSerializerOptions options)
  1029. {
  1030. if(value is null)
  1031. {
  1032. writer.WriteNullValue();
  1033. }
  1034. else if(value.GetType() == typeof(object))
  1035. {
  1036. writer.WriteStartObject();
  1037. writer.WriteEndObject();
  1038. }
  1039. else
  1040. {
  1041. // Call the serialiser, but this time with the value's real type, so this particular won't get called (since this only
  1042. // gets called if the type passed into 'Serialize' is *identically* 'object'.
  1043. JsonSerializer.Serialize(writer, value, value.GetType(), options);
  1044. }
  1045. }
  1046. }
  1047. public class TypeJsonConverter : CustomJsonConverter<Type>
  1048. {
  1049. public override Type? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
  1050. {
  1051. if(reader.TokenType == JsonTokenType.String)
  1052. {
  1053. return Type.GetType(reader.GetString());
  1054. }
  1055. else
  1056. {
  1057. return null;
  1058. }
  1059. }
  1060. public override void Write(Utf8JsonWriter writer, Type value, JsonSerializerOptions options)
  1061. {
  1062. writer.WriteStringValue(value.AssemblyQualifiedName);
  1063. }
  1064. }
  1065. public class ValueTupleJsonConverter : CustomJsonConverter<object>
  1066. {
  1067. public override bool CanConvert(Type typeToConvert)
  1068. {
  1069. return typeToConvert.HasInterface<ITuple>() && !typeToConvert.IsClass && !typeToConvert.IsInterface;
  1070. }
  1071. public override object? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
  1072. {
  1073. var typeArgs = typeToConvert.GenericTypeArguments;
  1074. var values = new object?[typeArgs.Length];
  1075. ReadObject(ref reader, (ref Utf8JsonReader reader, string property) =>
  1076. {
  1077. if (property.StartsWith("Item") && int.TryParse(property[4..], out var i) && i - 1 < values.Length)
  1078. {
  1079. values[i - 1] = JsonSerializer.Deserialize(ref reader, typeArgs[i - 1], options);
  1080. }
  1081. else if(property == "Rest")
  1082. {
  1083. values[^1] = JsonSerializer.Deserialize(ref reader, typeArgs[^1], options);
  1084. }
  1085. });
  1086. return Activator.CreateInstance(typeToConvert, values);
  1087. }
  1088. public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options)
  1089. {
  1090. if(value is ITuple tuple)
  1091. {
  1092. var type = value.GetType();
  1093. writer.WriteStartObject();
  1094. foreach(var field in type.GetFields())
  1095. {
  1096. writer.WritePropertyName(field.Name);
  1097. JsonSerializer.Serialize(writer, field.GetValue(tuple), field.FieldType, options);
  1098. }
  1099. writer.WriteEndObject();
  1100. }
  1101. else
  1102. {
  1103. writer.WriteNullValue();
  1104. return;
  1105. }
  1106. }
  1107. }
  1108. }