diff --git a/Example.Backend/Program.cs b/Example.Backend/Program.cs index 06bec62..30b50f6 100644 --- a/Example.Backend/Program.cs +++ b/Example.Backend/Program.cs @@ -1,6 +1,7 @@ using System.Net; using Example.Backend; using mROA.Abstract; +using mROA.Cbor; using mROA.Codegen; using mROA.Implementation; using mROA.Implementation.Backend; @@ -12,7 +13,8 @@ class Program public static void Main(string[] args) { var builder = new FullMixBuilder(); - builder.UseJsonSerialisation(); + // builder.UseJsonSerialisation(); + builder.Modules.Add(new CborSerializationToolkit()); builder.Modules.Add(new BackendIdentityGenerator()); builder.UseNetworkGateway(new IPEndPoint(IPAddress.Loopback, 4567), typeof(NextGenerationInteractionModule), builder.GetModule()!); diff --git a/Example.Frontend/Program.cs b/Example.Frontend/Program.cs index 98d4f5f..93e5c81 100644 --- a/Example.Frontend/Program.cs +++ b/Example.Frontend/Program.cs @@ -5,6 +5,7 @@ using System.Threading; using System.Threading.Tasks; using Example.Frontend; using Example.Shared; +using mROA.Cbor; using mROA.Codegen; using mROA.Implementation; using mROA.Implementation.Backend; @@ -17,7 +18,9 @@ class Program { var builder = new FullMixBuilder(); new RemoteTypeBinder(); - builder.Modules.Add(new JsonSerializationToolkit()); + // builder.Modules.Add(new JsonSerializationToolkit()); + builder.Modules.Add(new CborSerializationToolkit()); + builder.Modules.Add(new RemoteContextRepository()); builder.Modules.Add(new NextGenerationInteractionModule()); builder.Modules.Add(new RepresentationModule()); diff --git a/Example.Shared/Example.Shared.csproj b/Example.Shared/Example.Shared.csproj index 3d97856..f2050a6 100644 --- a/Example.Shared/Example.Shared.csproj +++ b/Example.Shared/Example.Shared.csproj @@ -9,6 +9,7 @@ + diff --git a/mROA.Cbor/CborSerializationToolkit.cs b/mROA.Cbor/CborSerializationToolkit.cs index af66e5e..3b7da43 100644 --- a/mROA.Cbor/CborSerializationToolkit.cs +++ b/mROA.Cbor/CborSerializationToolkit.cs @@ -26,38 +26,43 @@ namespace mROA.Cbor writer.Encode(destination); } - public T Deserialize(byte[] rawData, IEndPointContext context) + public T Deserialize(byte[] rawData, IEndPointContext? context) { - return (T)Deserialize(rawData, typeof(T), context); + return (T)Deserialize(rawData, typeof(T), context) ?? default; } - public object Deserialize(byte[] rawData, Type type, IEndPointContext context) + public object? Deserialize(byte[] rawData, Type type, IEndPointContext? context) { return Deserialize(rawData.AsMemory(), type, context); } - public T Deserialize(ReadOnlyMemory rawData, IEndPointContext context) + public T Deserialize(ReadOnlyMemory rawMemory, IEndPointContext? context) { - return (T)Deserialize(rawData, typeof(T), context); + return (T)Deserialize(rawMemory, typeof(T), context); } - public object Deserialize(ReadOnlyMemory rawData, Type type, IEndPointContext context) + public object? Deserialize(ReadOnlyMemory rawMemory, Type type, IEndPointContext? context) { - var reader = new CborReader(rawData); + var reader = new CborReader(rawMemory); return ReadData(reader, type, context); } - public T Cast(object nonCasted, IEndPointContext context) + public T Cast(object nonCasted, IEndPointContext? context) { return (T)Cast(nonCasted, typeof(T), context); } - public object Cast(object nonCasted, Type type, IEndPointContext context) + public object Cast(object nonCasted, Type type, IEndPointContext? context) { + if (nonCasted.GetType() == type) + return nonCasted; + + if (nonCasted is PreParsedValue preParsed) + return preParsed.ToObject(type, context); return null; } - private void WriteData(object? obj, CborWriter writer, IEndPointContext context) + private void WriteData(object? obj, CborWriter writer, IEndPointContext? context) { switch (obj) { @@ -94,6 +99,9 @@ namespace mROA.Cbor case DateTimeOffset dto: writer.WriteDateTimeOffset(dto); break; + case Guid g: + writer.WriteByteString(g.ToByteArray()); + break; case byte[] bytes: writer.WriteByteString(bytes); break; @@ -104,13 +112,14 @@ namespace mROA.Cbor WriteList(enumerable, writer, context); break; case ISharedObject sharedObject: - sharedObject.EndPointContext = context; + if (context != null) + sharedObject.EndPointContext = context; WriteObject(sharedObject, writer, context); break; default: if (obj.GetType().IsEnum) { - writer.WriteUInt32((uint)obj); + writer.WriteInt32((int)obj); break; } @@ -119,7 +128,7 @@ namespace mROA.Cbor } } - private void WriteList(IList list, CborWriter writer, IEndPointContext context) + private void WriteList(IList list, CborWriter writer, IEndPointContext? context) { writer.WriteStartArray(list.Count); @@ -129,7 +138,7 @@ namespace mROA.Cbor writer.WriteEndArray(); } - private void WriteDictionary(IDictionary dictionary, CborWriter writer, IEndPointContext context) + private void WriteDictionary(IDictionary dictionary, CborWriter writer, IEndPointContext? context) { writer.WriteStartMap(dictionary.Count); var keysEnumerator = dictionary.Keys.GetEnumerator(); @@ -147,7 +156,7 @@ namespace mROA.Cbor (valuesEnumerator as IDisposable)?.Dispose(); } - private void WriteObject(object obj, CborWriter writer, IEndPointContext context) + private void WriteObject(object obj, CborWriter writer, IEndPointContext? context) { var type = obj.GetType(); var properties = FilterProperties(type.GetProperties()); @@ -155,7 +164,7 @@ namespace mROA.Cbor WriteList(values, writer, context); } - private object ReadData(CborReader reader, Type? type, IEndPointContext context) + private object? ReadData(CborReader reader, Type? type, IEndPointContext? context) { var state = reader.PeekState(); switch (state) @@ -164,20 +173,24 @@ namespace mROA.Cbor return reader.ReadBoolean(); case CborReaderState.UnsignedInteger: case CborReaderState.NegativeInteger: - if (type == typeof(int)) + if (type == typeof(int) || type is { IsEnum: true }) return reader.ReadInt32(); if (type == typeof(long)) return reader.ReadInt64(); - if (type == typeof(uint) || type.IsEnum) + if (type == typeof(uint)) return reader.ReadUInt32(); if (type == typeof(ulong)) return reader.ReadUInt64(); - break; + + return reader.ReadInt32(); case CborReaderState.ByteString: + if (type == typeof(Guid)) + return new Guid(reader.ReadByteString()); return reader.ReadByteString(); case CborReaderState.TextString: return reader.ReadTextString(); case CborReaderState.Null: + reader.ReadNull(); return null; case CborReaderState.DoublePrecisionFloat: return reader.ReadDouble(); @@ -207,36 +220,51 @@ namespace mROA.Cbor } - private Array ReadList(CborReader reader, Type? type, IEndPointContext context) + private IList ReadList(CborReader reader, Type? type, IEndPointContext? context) { var length = reader.ReadStartArray(); if (length != null) { var elementType = typeof(object); - if (type is { IsArray: true }) + { elementType = type.GetElementType(); - else if (typeof(IList).IsAssignableFrom(type)) + Array values = Array.CreateInstance(elementType, length.Value); + + + for (int i = 0; i < length; i++) + { + values.SetValue(ReadData(reader, elementType, context), i); + } + + reader.ReadEndArray(); + return values; + } + + if (typeof(IList).IsAssignableFrom(type)) elementType = type.GetGenericArguments()[0]; - - Array values = Array.CreateInstance(elementType, length.Value); - + else elementType = typeof(object); + Type genericListType = typeof(List<>).MakeGenericType(elementType); + var list = (IList)Activator.CreateInstance(genericListType, length); for (int i = 0; i < length; i++) { - values.SetValue(ReadData(reader, elementType, context), i); + list.Add(ReadData(reader, elementType, context)); } reader.ReadEndArray(); - return values; + + + return list; + + + return null; } - reader.ReadEndArray(); - - return Array.Empty(); + return null; } - private IDictionary ReadDictionary(CborReader reader, Type type, IEndPointContext context) + private IDictionary ReadDictionary(CborReader reader, Type type, IEndPointContext? context) { var dictionaryInstance = (Activator.CreateInstance(type) as IDictionary)!; var length = reader.ReadStartArray(); @@ -253,8 +281,13 @@ namespace mROA.Cbor return dictionaryInstance; } - private object ReadObject(CborReader reader, Type type, IEndPointContext context) + private object ReadObject(CborReader reader, Type type, IEndPointContext? context) { + if (type == typeof(object)) + { + return new PreParsedValue(ReadList(reader, null, context) as List); + } + var instance = Activator.CreateInstance(type)!; FillObject(instance, type, reader, context); @@ -262,32 +295,50 @@ namespace mROA.Cbor return instance; } - private ISharedObject ReadSharedObject(CborReader reader, Type type, IEndPointContext context) + private ISharedObject ReadSharedObject(CborReader reader, Type type, IEndPointContext? context) { var sharedObject = (Activator.CreateInstance(type) as ISharedObject)!; - sharedObject.EndPointContext = context; + if (context != null) + { + sharedObject.EndPointContext = context; + } FillObject(sharedObject, type, reader, context); return sharedObject; } - private void FillObject(object obj, Type type, CborReader reader, IEndPointContext context) + private void FillObject(object obj, Type type, CborReader reader, IEndPointContext? context) { - var properties = FilterProperties(type.GetProperties()); - - _ = reader.ReadStartArray(); - - foreach (var property in properties) + try { - var value = ReadData(reader, property.PropertyType, context); - property.SetValue(obj, value); - } + var propertyInfos = type.GetProperties(); + var properties = FilterProperties(propertyInfos); - reader.ReadEndArray(); + var length = reader.ReadStartArray(); +#if TRACE + Console.WriteLine($"Reading list of {length} objects, {properties.Count} properties found"); +#endif + + for (var index = 0; index < length; index++) + { + var property = properties[index]; + var value = ReadData(reader, property.PropertyType, context); + property.SetValue(obj, value); + } + + reader.ReadEndArray(); + } + catch (Exception e) + { + Console.WriteLine(e); + reader.ReadEndArray(); + + throw; + } } - private List FilterProperties(PropertyInfo[] properties) + public static List FilterProperties(PropertyInfo[] properties) { var finalProperties = new List(properties.Length); foreach (var property in properties) @@ -298,5 +349,49 @@ namespace mROA.Cbor return finalProperties; } + + public void Inject(T dependency) + { + } + + public byte[] Serialize(T objectToSerialize) + { + return Serialize(objectToSerialize, typeof(T)); + } + + public byte[] Serialize(object objectToSerialize, Type type) + { + return Serialize(objectToSerialize, context: null); + } + + public T Deserialize(byte[] rawData) + { + return Deserialize(rawData: rawData, context: null); + } + + public object? Deserialize(byte[] rawData, Type type) + { + return Deserialize(rawData: rawData, type, context: null); + } + + public T Deserialize(Span rawData) + { + return Deserialize(rawData.ToArray().AsMemory(), context: null); + } + + public object? Deserialize(Span rawData, Type type) + { + return Deserialize(rawData: rawData.ToArray(), type: type); + } + + public T Cast(object nonCasted) + { + return Cast(nonCasted: nonCasted, context: null); + } + + public object Cast(object nonCasted, Type type) + { + return Cast(nonCasted: nonCasted, type: type, context: null); + } } } \ No newline at end of file diff --git a/mROA.Cbor/IContextualSerializationToolKit.cs b/mROA.Cbor/IContextualSerializationToolKit.cs index c7ee66c..957ca29 100644 --- a/mROA.Cbor/IContextualSerializationToolKit.cs +++ b/mROA.Cbor/IContextualSerializationToolKit.cs @@ -3,15 +3,15 @@ using mROA.Abstract; namespace mROA.Cbor { - public interface IContextualSerializationToolKit + public interface IContextualSerializationToolKit : ISerializationToolkit { - byte[] Serialize(object objectToSerialize, IEndPointContext context); - void Serialize(object objectToSerialize, Span destination, IEndPointContext context); - T Deserialize(byte[] rawData, IEndPointContext context); - object Deserialize(byte[] rawData, Type type, IEndPointContext context); - T Deserialize(ReadOnlyMemory rawData, IEndPointContext context); - object Deserialize(ReadOnlyMemory rawData, Type type, IEndPointContext context); - T Cast(object nonCasted, IEndPointContext context); - object Cast(object nonCasted, Type type, IEndPointContext context); + byte[] Serialize(object objectToSerialize, IEndPointContext? context); + void Serialize(object objectToSerialize, Span destination, IEndPointContext? context); + T Deserialize(byte[] rawData, IEndPointContext? context); + object? Deserialize(byte[] rawData, Type type, IEndPointContext? context); + T Deserialize(ReadOnlyMemory rawMemory, IEndPointContext? context); + object? Deserialize(ReadOnlyMemory rawMemory, Type type, IEndPointContext? context); + T Cast(object nonCasted, IEndPointContext? context); + object? Cast(object nonCasted, Type type, IEndPointContext? context); } } \ No newline at end of file diff --git a/mROA.Cbor/PreParsedValue.cs b/mROA.Cbor/PreParsedValue.cs new file mode 100644 index 0000000..e8e6379 --- /dev/null +++ b/mROA.Cbor/PreParsedValue.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using mROA.Abstract; +using mROA.Implementation; + +namespace mROA.Cbor +{ + public class PreParsedValue + { + public List Properties { get; set; } + + public PreParsedValue(List properties) + { + Properties = properties; + } + + public object? ToObject(Type type, IEndPointContext? context) + { + var instance = Activator.CreateInstance(type); + if (instance == null) + return null; + + if (instance is ISharedObject sharedObject && context != null) + { + sharedObject.EndPointContext = context; + } + + var properties = CborSerializationToolkit.FilterProperties(type.GetProperties()); + for (var index = 0; index < properties.Count; index++) + { + var property = properties[index]; + property.SetValue(instance, Properties[index]); + } + return instance; + } + } +} \ No newline at end of file diff --git a/mROA.Test/CborTest.cs b/mROA.Test/CborTest.cs index a6d1bf2..cfa7d20 100644 --- a/mROA.Test/CborTest.cs +++ b/mROA.Test/CborTest.cs @@ -21,6 +21,7 @@ public class CborTest IntValue = 123, DoubleValue = 3.14159, StringValue = "abc", + EnumValue = TestEnum.X, CollectionElements = [ _basicCollectionElement, @@ -69,6 +70,7 @@ public class CborTest public int IntValue { get; set; } public double DoubleValue { get; set; } public string StringValue { get; set; } + public TestEnum EnumValue { get; set; } public int[] IntArray { get; set; } public List CollectionElements { get; set; } @@ -115,4 +117,9 @@ public class CborTest return HashCode.Combine(A, B, C); } } + + public enum TestEnum + { + X = -5, Y, Z + } } \ No newline at end of file diff --git a/mROA/Abstract/ISerializationToolkit.cs b/mROA/Abstract/ISerializationToolkit.cs index fbb48d2..b3502f8 100644 --- a/mROA/Abstract/ISerializationToolkit.cs +++ b/mROA/Abstract/ISerializationToolkit.cs @@ -10,8 +10,8 @@ namespace mROA.Abstract object? Deserialize(byte[] rawData, Type type); T? Deserialize(Span rawData); object? Deserialize(Span rawData, Type type); - T Cast(object nonCasted); - object Cast(object nonCasted, Type type); + T? Cast(object nonCasted); + object? Cast(object nonCasted, Type type); } } \ No newline at end of file diff --git a/mROA/Implementation/CallRequest.cs b/mROA/Implementation/CallRequest.cs index 1d51e7e..db38e5e 100644 --- a/mROA/Implementation/CallRequest.cs +++ b/mROA/Implementation/CallRequest.cs @@ -1,5 +1,6 @@ using System; using System.Text.Json.Serialization; +using mROA.Implementation.Attributes; // ReSharper disable UnusedAutoPropertyAccessor.Global // ReSharper disable AutoPropertyCanBeMadeGetOnly.Global @@ -20,7 +21,7 @@ namespace mROA.Implementation public int CommandId { get; set; } public int ObjectId { get; set; } = -1; - [JsonIgnore] + [SerializationIgnore] public Type? ParameterType { get; set; } public object? Parameter { get; set; } diff --git a/mROA/Implementation/CommandExecution/FinalCommandExecution.cs b/mROA/Implementation/CommandExecution/FinalCommandExecution.cs index 5fa3f31..331928d 100644 --- a/mROA/Implementation/CommandExecution/FinalCommandExecution.cs +++ b/mROA/Implementation/CommandExecution/FinalCommandExecution.cs @@ -1,6 +1,7 @@ using System; using System.Text.Json.Serialization; using mROA.Abstract; +using mROA.Implementation.Attributes; // ReSharper disable UnusedAutoPropertyAccessor.Global @@ -9,9 +10,9 @@ namespace mROA.Implementation.CommandExecution public class FinalCommandExecution : ICommandExecution { public Guid Id { get; set; } - [JsonIgnore] + [SerializationIgnore] public int ClientId { get; set; } - [JsonIgnore] + [SerializationIgnore] public int CommandId { get; set; } } diff --git a/mROA/Implementation/CommandExecution/TypedFinalCommandExecution.cs b/mROA/Implementation/CommandExecution/TypedFinalCommandExecution.cs index b998737..47ff4b1 100644 --- a/mROA/Implementation/CommandExecution/TypedFinalCommandExecution.cs +++ b/mROA/Implementation/CommandExecution/TypedFinalCommandExecution.cs @@ -1,11 +1,12 @@ using System; using System.Text.Json.Serialization; +using mROA.Implementation.Attributes; namespace mROA.Implementation.CommandExecution { public class TypedFinalCommandExecution : FinalCommandExecution { - [JsonIgnore] + [SerializationIgnore] // ReSharper disable once UnusedAutoPropertyAccessor.Global public Type? Type { get; set; } } diff --git a/mROA/Implementation/SharedObject.cs b/mROA/Implementation/SharedObject.cs index 84d18bb..2eb3b47 100644 --- a/mROA/Implementation/SharedObject.cs +++ b/mROA/Implementation/SharedObject.cs @@ -2,6 +2,7 @@ using System.Text.Json.Serialization; using System.Threading.Tasks; using mROA.Abstract; +using mROA.Implementation.Attributes; // ReSharper disable UnusedMember.Global #pragma warning disable CS8618, CS9264 @@ -41,7 +42,7 @@ namespace mROA.Implementation public class SharedObject : ISharedObject where T : notnull { - [JsonIgnore] + [SerializationIgnore] public IEndPointContext EndPointContext { get; set; } = new EndPointContext { RealRepository = TransmissionConfig.RealContextRepository, @@ -88,7 +89,7 @@ namespace mROA.Implementation } } - [JsonIgnore] + [SerializationIgnore] public T Value { get; private set; } // ReSharper disable once MemberCanBePrivate.Global