| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587 | using System;using System.Collections.Generic;using System.Diagnostics.CodeAnalysis;using System.IO;using System.Linq;using System.Reflection;using System.Runtime.InteropServices.ComTypes;using System.Threading;using System.Xml.Linq;using InABox.Clients;using JetBrains.Annotations;using Newtonsoft.Json;using Newtonsoft.Json.Linq;namespace InABox.Core{    public interface ISerializeBinary    {        public void SerializeBinary(BinaryWriter writer);        public void DeserializeBinary(BinaryReader reader);    }    public static class Serialization    {        private static JsonSerializerSettings? _serializerSettings;        private static JsonSerializerSettings SerializerSettings(bool indented = true)        {            if (_serializerSettings == null)            {                _serializerSettings = new JsonSerializerSettings                {                    DateParseHandling = DateParseHandling.DateTime,                    DateFormatHandling = DateFormatHandling.IsoDateFormat,                    DateTimeZoneHandling = DateTimeZoneHandling.RoundtripKind                };                _serializerSettings.Converters.Add(new DataTableJsonConverter());                //serializerSettings.Converters.Add(new DateTimeJsonConverter());                _serializerSettings.Converters.Add(new FilterJsonConverter());                _serializerSettings.Converters.Add(new ColumnJsonConverter());                _serializerSettings.Converters.Add(new SortOrderJsonConverter());                _serializerSettings.Converters.Add(new UserPropertiesJsonConverter());            }            _serializerSettings.Formatting = indented ? Formatting.Indented : Formatting.None;            return _serializerSettings;        }        public static string Serialize(object? o, bool indented = false)        {            var json = JsonConvert.SerializeObject(o, SerializerSettings(indented));            return json;        }        public static void Serialize(object o, Stream stream, bool indented = false)        {            var settings = SerializerSettings(indented);            using (var sw = new StreamWriter(stream))            {                using (JsonWriter writer = new JsonTextWriter(sw))                {                    var serializer = JsonSerializer.Create(settings);                    serializer.Serialize(writer, o);                }            }        }        public static void DeserializeInto(string json, object target)        {            JsonConvert.PopulateObject(json, target, SerializerSettings());        }        [return: MaybeNull]        public static T Deserialize<T>(Stream? stream, bool strict = false)        {            if (stream == null)                return default;            try            {                var settings = SerializerSettings();                using var sr = new StreamReader(stream);                using JsonReader reader = new JsonTextReader(sr);                var serializer = JsonSerializer.Create(settings);                return serializer.Deserialize<T>(reader);            }            catch (Exception e)            {                if (strict)                    throw;                Logger.Send(LogType.Error, ClientFactory.UserID, $"Error in Deserialize<{typeof(T)}>(): {e.Message}");                return default;            }        }        public static object? Deserialize(Type type, Stream? stream)        {            if (stream == null)                return null;            object? result = null;            var settings = SerializerSettings();            using (var sr = new StreamReader(stream))            {                using (JsonReader reader = new JsonTextReader(sr))                {                    var serializer = JsonSerializer.Create(settings);                    result = serializer.Deserialize(reader, type);                }            }            return result;        }        [return: MaybeNull]        public static T Deserialize<T>(string? json, bool strict = false) // where T : new()        {            var ret = default(T);            if (string.IsNullOrWhiteSpace(json))                return ret;            try            {                var settings = SerializerSettings();                //if (typeof(T).IsSubclassOf(typeof(BaseObject)))                //{                //    ret = Activator.CreateInstance<T>();                //    (ret as BaseObject).SetObserving(false);                //    JsonConvert.PopulateObject(json, ret, settings);                //    (ret as BaseObject).SetObserving(true);                //}                //else                 if (typeof(T).IsArray)                {                    object o = Array.CreateInstance(typeof(T).GetElementType(), 0);                    ret = (T)o;                }                else                {                    ret = JsonConvert.DeserializeObject<T>(json, settings);                }            }            catch (Exception)            {                if (strict)                {                    throw;                }                ret = Activator.CreateInstance<T>();            }            return ret;        }        public static object? Deserialize(Type T, string json) // where T : new()        {            var ret = T.GetDefault();            if (string.IsNullOrWhiteSpace(json))                return ret;            try            {                var settings = SerializerSettings();                //if (typeof(T).IsSubclassOf(typeof(BaseObject)))                //{                //    ret = Activator.CreateInstance<T>();                //    (ret as BaseObject).SetObserving(false);                //    JsonConvert.PopulateObject(json, ret, settings);                //    (ret as BaseObject).SetObserving(true);                //}                //else                 if (T.IsArray)                {                    object o = Array.CreateInstance(T.GetElementType(), 0);                    ret = o;                }                else                {                    ret = JsonConvert.DeserializeObject(json, T, settings);                }            }            catch (Exception)            {                ret = Activator.CreateInstance(T);            }            return ret;        }        #region Binary Serialization        public static byte[] WriteBinary(this ISerializeBinary obj)        {            using var stream = new MemoryStream();            obj.SerializeBinary(new BinaryWriter(stream));            return stream.ToArray();        }        public static T ReadBinary<T>(byte[] data)            where T : ISerializeBinary, new() => (T)ReadBinary(typeof(T), data);        public static T ReadBinary<T>(Stream stream)            where T : ISerializeBinary, new() => (T)ReadBinary(typeof(T), stream);        public static object ReadBinary(Type T, byte[] data)        {            using var stream = new MemoryStream(data);            return ReadBinary(T, stream);        }        public static object ReadBinary(Type T, Stream stream)        {            var obj = (Activator.CreateInstance(T) as ISerializeBinary)!;            obj.DeserializeBinary(new BinaryReader(stream));            return obj;        }        #endregion    }    public static class SerializationUtils    {        public static void Write(this BinaryWriter writer, Guid guid)        {            writer.Write(guid.ToByteArray());        }        public static Guid ReadGuid(this BinaryReader reader)        {            return new Guid(reader.ReadBytes(16));        }        /// <summary>        /// Binary serialize a bunch of different types of values. <see cref="WriteBinaryValue(BinaryWriter, Type, object?)"/> and        /// <see cref="ReadBinaryValue(BinaryReader, Type)"/> are inverses of each other.        /// </summary>        /// <remarks>        /// Handles <see cref="byte"/>[], <see cref="Array"/>s of serialisable values, <see cref="Enum"/>, <see cref="bool"/>, <see cref="string"/>,        /// <see cref="Guid"/>, <see cref="byte"/>, <see cref="Int16"/>, <see cref="Int32"/>, <see cref="Int64"/>, <see cref="float"/>, <see cref="double"/>,        /// <see cref="DateTime"/>, <see cref="TimeSpan"/>, <see cref="LoggablePropertyAttribute"/>, <see cref="IPackable"/>, <see cref="Nullable{T}"/>        /// and <see cref="ISerializeBinary"/>.        /// </remarks>        /// <param name="writer"></param>        /// <param name="type"></param>        /// <param name="value"></param>        /// <exception cref="Exception">If an object of <paramref name="type"/> is unable to be serialized.</exception>        public static void WriteBinaryValue(this BinaryWriter writer, Type type, object? value)        {            value ??= CoreUtils.GetDefault(type);            if (type == typeof(byte[]) && value is byte[] bArray)            {                writer.Write(bArray.Length);                writer.Write(bArray);            }            else if (type == typeof(byte[]) && value is null)            {                writer.Write(0);            }            else if (type.IsArray && value is Array array)            {                var elementType = type.GetElementType();                writer.Write(array.Length);                foreach (var val1 in array)                {                    WriteBinaryValue(writer, elementType, val1);                }            }            else if (type.IsArray && value is null)            {                writer.Write(0);            }            else if (type.IsEnum && value is Enum e)            {                var underlyingType = type.GetEnumUnderlyingType();                WriteBinaryValue(writer, underlyingType, Convert.ChangeType(e, underlyingType));            }            else if (type == typeof(bool) && value is bool b)            {                writer.Write(b);            }            else if (type == typeof(string) && value is string str)            {                writer.Write(str);            }            else if (type == typeof(string) && value is null)            {                writer.Write("");            }            else if (type == typeof(Guid) && value is Guid guid)            {                writer.Write(guid);            }            else if (type == typeof(byte) && value is byte i8)            {                writer.Write(i8);            }            else if (type == typeof(Int16) && value is Int16 i16)            {                writer.Write(i16);            }            else if (type == typeof(Int32) && value is Int32 i32)            {                writer.Write(i32);            }            else if (type == typeof(Int64) && value is Int64 i64)            {                writer.Write(i64);            }            else if (type == typeof(float) && value is float f32)            {                writer.Write(f32);            }            else if (type == typeof(double) && value is double f64)            {                writer.Write(f64);            }            else if (type == typeof(DateTime) && value is DateTime date)            {                writer.Write(date.Ticks);            }            else if (type == typeof(TimeSpan) && value is TimeSpan time)            {                writer.Write(time.Ticks);            }            else if (type == typeof(LoggablePropertyAttribute))            {                writer.Write((value as LoggablePropertyAttribute)?.Format ?? "");            }            else if (typeof(IPackable).IsAssignableFrom(type) && value is IPackable pack)            {                pack.Pack(writer);            }            else if (typeof(ISerializeBinary).IsAssignableFrom(type) && value is ISerializeBinary binary)            {                binary.SerializeBinary(writer);            }            else if (Nullable.GetUnderlyingType(type) is Type t)            {                if(value == null)                {                    writer.Write(false);                }                else                {                    writer.Write(true);                    writer.WriteBinaryValue(t, value);                }            }            else            {                throw new Exception($"Invalid type; Target DataType is {type} and value DataType is {value?.GetType().ToString() ?? "null"}");            }        }        /// <summary>        /// Binary deserialize a bunch of different types of values. <see cref="WriteBinaryValue(BinaryWriter, Type, object?)"/> and        /// <see cref="ReadBinaryValue(BinaryReader, Type)"/> are inverses of each other.        /// </summary>        /// <remarks>        /// Handles <see cref="byte"/>[], <see cref="Array"/>s of serialisable values, <see cref="Enum"/>, <see cref="bool"/>, <see cref="string"/>,        /// <see cref="Guid"/>, <see cref="byte"/>, <see cref="Int16"/>, <see cref="Int32"/>, <see cref="Int64"/>, <see cref="float"/>, <see cref="double"/>,        /// <see cref="DateTime"/>, <see cref="TimeSpan"/>, <see cref="LoggablePropertyAttribute"/>, <see cref="IPackable"/>, <see cref="Nullable{T}"/>        /// and <see cref="ISerializeBinary"/>.        /// </remarks>        /// <param name="writer"></param>        /// <param name="type"></param>        /// <param name="value"></param>        /// <exception cref="Exception">If an object of <paramref name="type"/> is unable to be deserialized.</exception>        public static object? ReadBinaryValue(this BinaryReader reader, Type type)        {            if (type == typeof(byte[]))            {                var length = reader.ReadInt32();                return reader.ReadBytes(length);            }            else if (type.IsArray)            {                var length = reader.ReadInt32();                var elementType = type.GetElementType();                var array = Array.CreateInstance(elementType, length);                for (int i = 0; i < array.Length; ++i)                {                    array.SetValue(ReadBinaryValue(reader, elementType), i);                }                return array;            }            else if (type.IsEnum)            {                var val = ReadBinaryValue(reader, type.GetEnumUnderlyingType());                return Enum.ToObject(type, val);            }            else if (type == typeof(bool))            {                return reader.ReadBoolean();            }            else if (type == typeof(string))            {                return reader.ReadString();            }            else if (type == typeof(Guid))            {                return reader.ReadGuid();            }            else if (type == typeof(byte))            {                return reader.ReadByte();            }            else if (type == typeof(Int16))            {                return reader.ReadInt16();            }            else if (type == typeof(Int32))            {                return reader.ReadInt32();            }            else if (type == typeof(Int64))            {                return reader.ReadInt64();            }            else if (type == typeof(float))            {                return reader.ReadSingle();            }            else if (type == typeof(double))            {                return reader.ReadDouble();            }            else if (type == typeof(DateTime))            {                return new DateTime(reader.ReadInt64());            }            else if (type == typeof(TimeSpan))            {                return new TimeSpan(reader.ReadInt64());            }            else if (type == typeof(LoggablePropertyAttribute))            {                String format = reader.ReadString();                return String.IsNullOrWhiteSpace(format)                    ? null                    : new LoggablePropertyAttribute() { Format = format };            }            else if (typeof(IPackable).IsAssignableFrom(type))            {                var packable = (Activator.CreateInstance(type) as IPackable)!;                packable.Unpack(reader);                return packable;            }            else if (typeof(ISerializeBinary).IsAssignableFrom(type))            {                var obj = (Activator.CreateInstance(type) as ISerializeBinary)!;                obj.DeserializeBinary(reader);                return obj;            }            else if (Nullable.GetUnderlyingType(type) is Type t)            {                var isNull = reader.ReadBoolean();                if (isNull)                {                    return null;                }                else                {                    return reader.ReadBinaryValue(t);                }            }            else            {                throw new Exception($"Invalid type; Target DataType is {type}");            }        }        public static IEnumerable<IProperty> SerializableProperties(Type type) =>            DatabaseSchema.Properties(type)                .Where(x => !(x is StandardProperty st) || st.Property.GetCustomAttribute<DoNotSerialize>() == null);        /// <summary>        /// An implementation of binary serialising a <typeparamref name="TObject"/>; this is the inverse of <see cref="ReadObject{TObject}(BinaryReader)"/>.        /// </summary>        /// <remarks>        /// Also serialises the names of properties along with the values.        /// </remarks>        /// <typeparam name="TObject"></typeparam>        /// <param name="writer"></param>        /// <param name="entity"></param>        public static void WriteObject<TObject>(this BinaryWriter writer, TObject entity)            where TObject : BaseObject, new()        {            var properties = SerializableProperties(typeof(TObject)).ToList();            writer.Write(properties.Count);            foreach (var property in properties)            {                writer.Write(property.Name);                writer.WriteBinaryValue(property.PropertyType, property.Getter()(entity));            }        }        /// <summary>        /// The inverse of <see cref="WriteObject{TObject}(BinaryWriter, TObject)"/>.        /// </summary>        /// <typeparam name="TObject"></typeparam>        /// <param name="reader"></param>        /// <returns></returns>        public static TObject ReadObject<TObject>(this BinaryReader reader)            where TObject : BaseObject, new()        {            var obj = new TObject();            var nProps = reader.ReadInt32();            for(int i = 0; i < nProps; ++i)            {                var propName = reader.ReadString();                var property = DatabaseSchema.Property(typeof(TObject), propName);                property?.Setter()(obj, reader.ReadBinaryValue(property.PropertyType));            }            return obj;        }        /// <summary>        /// An implementation of binary serialising multiple <typeparamref name="TObject"/>s;        /// this is the inverse of <see cref="ReadObjects{TObject}(BinaryReader)"/>.        /// </summary>        /// <remarks>        /// Also serialises the names of properties along with the values.        /// </remarks>        /// <typeparam name="TObject"></typeparam>        /// <param name="writer"></param>        /// <param name="entity"></param>        public static void WriteObjects<TObject>(this BinaryWriter writer, ICollection<TObject> objects)            where TObject : BaseObject, new()        {            var properties = SerializableProperties(typeof(TObject)).ToList();            writer.Write(objects.Count);            writer.Write(properties.Count);            foreach (var property in properties)            {                writer.Write(property.Name);            }            foreach (var obj in objects)            {                foreach (var property in properties)                {                    writer.WriteBinaryValue(property.PropertyType, property.Getter()(obj));                }            }        }        /// <summary>        /// The inverse of <see cref="WriteObjects{TObject}(BinaryWriter, IList{TObject})"/>.        /// </summary>        /// <typeparam name="TObject"></typeparam>        /// <param name="reader"></param>        /// <returns></returns>        public static List<TObject> ReadObjects<TObject>(this BinaryReader reader)            where TObject : BaseObject, new()        {            var objs = new List<TObject>();            var properties = new List<IProperty>();            var nObjs = reader.ReadInt32();            var nProps = reader.ReadInt32();            for(int i = 0; i < nProps; ++i)            {                var property = reader.ReadString();                properties.Add(DatabaseSchema.Property(typeof(TObject), property));            }            for(int i = 0; i < nObjs; ++i)            {                var obj = new TObject();                foreach(var property in properties)                {                    property?.Setter()(obj, reader.ReadBinaryValue(property.PropertyType));                }                objs.Add(obj);            }            return objs;        }    }}
 |