|
@@ -19,7 +19,7 @@ namespace InABox.Core
|
|
|
|
|
|
public class SerialisationException : Exception
|
|
|
{
|
|
|
- public SerialisationException(string message): base(message) { }
|
|
|
+ public SerialisationException(string message) : base(message) { }
|
|
|
}
|
|
|
|
|
|
public interface ISerializeBinary
|
|
@@ -37,26 +37,26 @@ namespace InABox.Core
|
|
|
/// <param name="typeInfo"></param>
|
|
|
public static void WritablePropertiesOnly(JsonTypeInfo typeInfo)
|
|
|
{
|
|
|
- if(typeInfo.Kind == JsonTypeInfoKind.Object)
|
|
|
+ if (typeInfo.Kind == JsonTypeInfoKind.Object)
|
|
|
{
|
|
|
var toRemove = typeInfo.Properties.Where(x => x.Set is null).ToList();
|
|
|
- foreach(var prop in toRemove)
|
|
|
+ foreach (var prop in toRemove)
|
|
|
{
|
|
|
typeInfo.Properties.Remove(prop);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- private static JsonConverter[]? _converters;
|
|
|
-
|
|
|
- private static JsonConverter[] GetConverters()
|
|
|
+ public static List<JsonConverter> DefaultConverters { get; } = new List<JsonConverter>()
|
|
|
{
|
|
|
- _converters ??= CoreUtils.Entities.Where(x => typeof(JsonConverter).IsAssignableFrom(x) && x.HasInterface<IGlobalJsonConverter>())
|
|
|
- .Select(x => Activator.CreateInstance(x) as JsonConverter)
|
|
|
- .NotNull()
|
|
|
- .ToArray();
|
|
|
- return _converters;
|
|
|
- }
|
|
|
+ new CoreTableJsonConverter(),
|
|
|
+ new FilterJsonConverter(),
|
|
|
+ new ColumnJsonConverter(),
|
|
|
+ new ColumnsJsonConverter(),
|
|
|
+ new SortOrderJsonConverter(),
|
|
|
+ new UserPropertiesJsonConverter(),
|
|
|
+ new TypeJsonConverter(),
|
|
|
+ };
|
|
|
|
|
|
private static JsonSerializerOptions SerializerSettings(bool indented = true, bool populateObject = false)
|
|
|
{
|
|
@@ -67,7 +67,7 @@ namespace InABox.Core
|
|
|
{
|
|
|
var settings = new JsonSerializerOptions { };
|
|
|
|
|
|
- foreach (var converter in GetConverters())
|
|
|
+ foreach (var converter in DefaultConverters)
|
|
|
{
|
|
|
settings.Converters.Add(converter);
|
|
|
}
|
|
@@ -275,10 +275,10 @@ namespace InABox.Core
|
|
|
public JsonTypeInfo? GetTypeInfo(Type type, JsonSerializerOptions options)
|
|
|
{
|
|
|
var typeInfo = _jsonTypeInfoResolver?.GetTypeInfo(type, options);
|
|
|
- if(typeInfo != null && typeInfo.Kind != JsonTypeInfoKind.None)
|
|
|
+ if (typeInfo != null && typeInfo.Kind != JsonTypeInfoKind.None)
|
|
|
{
|
|
|
var defaultCreateObject = typeInfo.CreateObject;
|
|
|
- if(defaultCreateObject != null)
|
|
|
+ if (defaultCreateObject != null)
|
|
|
{
|
|
|
typeInfo.CreateObject = () =>
|
|
|
{
|
|
@@ -334,7 +334,7 @@ namespace InABox.Core
|
|
|
/// </remarks>
|
|
|
public class BinarySerializationSettings
|
|
|
{
|
|
|
-
|
|
|
+
|
|
|
/// <summary>
|
|
|
/// Should the Info() call return RPC and Rest Ports? This is
|
|
|
/// To workaround a bug in RPCsockets that crash on large uploads
|
|
@@ -343,7 +343,7 @@ namespace InABox.Core
|
|
|
/// True in all serialization versions >= 1.2
|
|
|
/// </remarks>
|
|
|
public bool RPCClientWorkaround { get; set; }
|
|
|
-
|
|
|
+
|
|
|
/// <summary>
|
|
|
/// Should reference types include a flag for nullability? (Adds an extra boolean field for whether the value is null or not).
|
|
|
/// </summary>
|
|
@@ -351,11 +351,11 @@ namespace InABox.Core
|
|
|
/// True in all serialisation versions >= 1.1.
|
|
|
/// </remarks>
|
|
|
public bool IncludeNullables { get; set; }
|
|
|
-
|
|
|
+
|
|
|
public string Version { get; set; }
|
|
|
|
|
|
public static BinarySerializationSettings Latest => V1_2;
|
|
|
-
|
|
|
+
|
|
|
|
|
|
|
|
|
public static BinarySerializationSettings V1_0 = new BinarySerializationSettings("1.0")
|
|
@@ -363,13 +363,13 @@ namespace InABox.Core
|
|
|
IncludeNullables = false,
|
|
|
RPCClientWorkaround = false
|
|
|
};
|
|
|
-
|
|
|
+
|
|
|
public static BinarySerializationSettings V1_1 = new BinarySerializationSettings("1.1")
|
|
|
{
|
|
|
IncludeNullables = true,
|
|
|
RPCClientWorkaround = false
|
|
|
};
|
|
|
-
|
|
|
+
|
|
|
public static BinarySerializationSettings V1_2 = new BinarySerializationSettings("1.2")
|
|
|
{
|
|
|
IncludeNullables = true,
|
|
@@ -411,8 +411,8 @@ namespace InABox.Core
|
|
|
}
|
|
|
|
|
|
private static bool MatchType<T1>(Type t) => typeof(T1) == t;
|
|
|
- private static bool MatchType<T1,T2>(Type t) => (typeof(T1) == t) || (typeof(T2) == t);
|
|
|
-
|
|
|
+ private static bool MatchType<T1, T2>(Type t) => (typeof(T1) == t) || (typeof(T2) == t);
|
|
|
+
|
|
|
/// <summary>
|
|
|
/// Binary serialize a bunch of different types of values. <see cref="WriteBinaryValue(CoreBinaryWriter, Type, object?)"/> and
|
|
|
/// <see cref="ReadBinaryValue(CoreBinaryReader, Type)"/> are inverses of each other.
|
|
@@ -430,7 +430,7 @@ namespace InABox.Core
|
|
|
public static void WriteBinaryValue(this CoreBinaryWriter writer, Type type, object? value)
|
|
|
{
|
|
|
value ??= CoreUtils.GetDefault(type);
|
|
|
-
|
|
|
+
|
|
|
if (value == null)
|
|
|
{
|
|
|
if (MatchType<string>(type))
|
|
@@ -446,13 +446,13 @@ namespace InABox.Core
|
|
|
else
|
|
|
writer.Write(0);
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
else if (MatchType<byte[], object>(type) && value is byte[] bArray)
|
|
|
{
|
|
|
writer.Write(bArray.Length);
|
|
|
writer.Write(bArray);
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
else if (type.IsArray && value is Array array)
|
|
|
{
|
|
|
var elementType = type.GetElementType();
|
|
@@ -462,77 +462,77 @@ namespace InABox.Core
|
|
|
WriteBinaryValue(writer, elementType, val1);
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
else if (type.IsEnum && value is Enum e)
|
|
|
{
|
|
|
var underlyingType = type.GetEnumUnderlyingType();
|
|
|
WriteBinaryValue(writer, underlyingType, Convert.ChangeType(e, underlyingType));
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
else if (MatchType<bool, object>(type) && value is bool b)
|
|
|
{
|
|
|
writer.Write(b);
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
else if (MatchType<string, object>(type) && value is string str)
|
|
|
writer.Write(str);
|
|
|
|
|
|
else if (MatchType<Guid, object>(type) && value is Guid guid)
|
|
|
writer.Write(guid);
|
|
|
-
|
|
|
+
|
|
|
else if (MatchType<byte, object>(type) && value is byte i8)
|
|
|
writer.Write(i8);
|
|
|
-
|
|
|
+
|
|
|
else if (MatchType<Int16, object>(type) && value is Int16 i16)
|
|
|
writer.Write(i16);
|
|
|
-
|
|
|
+
|
|
|
else if (MatchType<Int32, object>(type) && value is Int32 i32)
|
|
|
writer.Write(i32);
|
|
|
-
|
|
|
+
|
|
|
else if (MatchType<Int64, object>(type) && value is Int64 i64)
|
|
|
writer.Write(i64);
|
|
|
-
|
|
|
+
|
|
|
else if (MatchType<float, object>(type) && value is float f32)
|
|
|
writer.Write(f32);
|
|
|
-
|
|
|
+
|
|
|
else if (MatchType<double, object>(type) && value is double f64)
|
|
|
writer.Write(f64);
|
|
|
-
|
|
|
+
|
|
|
else if (MatchType<DateTime, object>(type) && value is DateTime date)
|
|
|
writer.Write(date.Ticks);
|
|
|
-
|
|
|
+
|
|
|
else if (MatchType<TimeSpan, object>(type) && value is TimeSpan time)
|
|
|
writer.Write(time.Ticks);
|
|
|
-
|
|
|
+
|
|
|
else if (MatchType<LoggablePropertyAttribute, object>(type) && value is LoggablePropertyAttribute lpa)
|
|
|
writer.Write(lpa.Format ?? string.Empty);
|
|
|
-
|
|
|
+
|
|
|
else if (typeof(IPackable).IsAssignableFrom(type) && value is IPackable pack)
|
|
|
{
|
|
|
if (writer.Settings.IncludeNullables)
|
|
|
writer.Write(true);
|
|
|
pack.Pack(writer);
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
else if (typeof(ISerializeBinary).IsAssignableFrom(type) && value is ISerializeBinary binary)
|
|
|
{
|
|
|
if (writer.Settings.IncludeNullables)
|
|
|
writer.Write(true);
|
|
|
binary.SerializeBinary(writer);
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
else if (Nullable.GetUnderlyingType(type) is Type t)
|
|
|
{
|
|
|
writer.Write(true);
|
|
|
writer.WriteBinaryValue(t, value);
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
else if (value is UserProperty userprop)
|
|
|
WriteBinaryValue(writer, userprop.Type, userprop.Value);
|
|
|
-
|
|
|
+
|
|
|
else
|
|
|
throw new SerialisationException($"Invalid type; Target DataType is {type} and value DataType is {value?.GetType().ToString() ?? "null"}");
|
|
|
-
|
|
|
+
|
|
|
}
|
|
|
|
|
|
public static void WriteBinaryValue<T>(this CoreBinaryWriter writer, T value)
|
|
@@ -817,7 +817,7 @@ namespace InABox.Core
|
|
|
writer.Write(property.Name);
|
|
|
}
|
|
|
|
|
|
- if(objects != null)
|
|
|
+ if (objects != null)
|
|
|
{
|
|
|
foreach (var obj in objects)
|
|
|
{
|
|
@@ -841,17 +841,17 @@ namespace InABox.Core
|
|
|
{
|
|
|
return ReadObjects<TObject>(reader, typeof(TObject));
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
public static List<TObject> ReadObjects<TObject>(this CoreBinaryReader reader, Type type) where TObject : BaseObject
|
|
|
{
|
|
|
if (!typeof(TObject).IsAssignableFrom(type))
|
|
|
throw new SerialisationException($"{type.EntityName()} is not a subclass of {typeof(TObject).EntityName()}");
|
|
|
-
|
|
|
+
|
|
|
var objs = new List<TObject>();
|
|
|
var properties = new List<IProperty>();
|
|
|
|
|
|
var nObjs = reader.ReadInt32();
|
|
|
- if(nObjs == 0)
|
|
|
+ if (nObjs == 0)
|
|
|
{
|
|
|
return objs;
|
|
|
}
|
|
@@ -861,7 +861,7 @@ namespace InABox.Core
|
|
|
{
|
|
|
var propertyName = reader.ReadString();
|
|
|
var property = DatabaseSchema.Property(type, propertyName)
|
|
|
- ?? throw new SerialisationException($"Property {propertyName} does not exist on {type.EntityName()}");
|
|
|
+ ?? throw new SerialisationException($"Property {propertyName} does not exist on {type.EntityName()}");
|
|
|
properties.Add(property);
|
|
|
}
|
|
|
|
|
@@ -884,15 +884,16 @@ namespace InABox.Core
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- public interface IGlobalJsonConverter
|
|
|
- {
|
|
|
- }
|
|
|
+ public interface IPolymorphicallySerialisable { }
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Adds a '$type' property to all classes that implement <see cref="IPolymorphicallySerialisable"/>.
|
|
|
+ /// </summary>
|
|
|
public class PolymorphicConverter : JsonConverter<object>
|
|
|
{
|
|
|
public override bool CanConvert(Type typeToConvert)
|
|
|
{
|
|
|
- return typeToConvert.IsInterface || typeToConvert.IsAbstract;
|
|
|
+ return typeof(IPolymorphicallySerialisable).IsAssignableFrom(typeToConvert) && (typeToConvert.IsInterface || typeToConvert.IsAbstract);
|
|
|
}
|
|
|
|
|
|
public override object? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
|
@@ -925,7 +926,7 @@ namespace InABox.Core
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- public abstract class CustomJsonConverter<T> : JsonConverter<T>, IGlobalJsonConverter
|
|
|
+ public abstract class CustomJsonConverter<T> : JsonConverter<T>
|
|
|
{
|
|
|
protected object? ReadJson(ref Utf8JsonReader reader)
|
|
|
{
|
|
@@ -1036,4 +1037,24 @@ namespace InABox.Core
|
|
|
WriteJson(writer, value);
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ public class TypeJsonConverter : CustomJsonConverter<Type>
|
|
|
+ {
|
|
|
+ public override Type? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
|
|
+ {
|
|
|
+ if(reader.TokenType == JsonTokenType.String)
|
|
|
+ {
|
|
|
+ return Type.GetType(reader.GetString());
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public override void Write(Utf8JsonWriter writer, Type value, JsonSerializerOptions options)
|
|
|
+ {
|
|
|
+ writer.WriteStringValue(value.AssemblyQualifiedName);
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|