Cbor часть 2, десериализация
This commit is contained in:
@@ -3,20 +3,28 @@ using System.Collections;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Formats.Cbor;
|
using System.Formats.Cbor;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text.Json;
|
||||||
using mROA.Abstract;
|
using mROA.Abstract;
|
||||||
using mROA.Implementation;
|
using mROA.Implementation;
|
||||||
|
using mROA.Implementation.Attributes;
|
||||||
|
|
||||||
namespace mROA.Cbor
|
namespace mROA.Cbor
|
||||||
{
|
{
|
||||||
public class CborSerializaitonToolkit : IContextualSerializationToolKit
|
public class CborSerializaitonToolkit : IContextualSerializationToolKit
|
||||||
{
|
{
|
||||||
public byte[] Serialize<T>(T objectToSerialize, IEndPointContext context)
|
public byte[] Serialize(object objectToSerialize, IEndPointContext context)
|
||||||
{
|
{
|
||||||
return Serialize(objectToSerialize, typeof(T), context);
|
var writer = new CborWriter();
|
||||||
|
WriteData(objectToSerialize, writer, context);
|
||||||
|
return writer.Encode();
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] Serialize(object objectToSerialize, Type type, IEndPointContext context)
|
public void Serialize(object objectToSerialize, Span<byte> destination, IEndPointContext context)
|
||||||
{
|
{
|
||||||
|
var writer = new CborWriter();
|
||||||
|
WriteData(objectToSerialize, writer, context);
|
||||||
|
writer.Encode(destination);
|
||||||
}
|
}
|
||||||
|
|
||||||
public T Deserialize<T>(byte[] rawData, IEndPointContext context)
|
public T Deserialize<T>(byte[] rawData, IEndPointContext context)
|
||||||
@@ -24,19 +32,20 @@ namespace mROA.Cbor
|
|||||||
return (T)Deserialize(rawData, typeof(T), context);
|
return (T)Deserialize(rawData, typeof(T), context);
|
||||||
}
|
}
|
||||||
|
|
||||||
public object? Deserialize(byte[] rawData, Type type, IEndPointContext context)
|
public object Deserialize(byte[] rawData, Type type, IEndPointContext context)
|
||||||
{
|
{
|
||||||
return Deserialize(rawData.AsSpan(), type, context);
|
return Deserialize(rawData.AsMemory(), type, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
public T Deserialize<T>(Span<byte> rawData, IEndPointContext context)
|
public T Deserialize<T>(ReadOnlyMemory<byte> rawData, IEndPointContext context)
|
||||||
{
|
{
|
||||||
return (T)Deserialize(rawData, typeof(T), context);
|
return (T)Deserialize(rawData, typeof(T), context);
|
||||||
}
|
}
|
||||||
|
|
||||||
public object? Deserialize(Span<byte> rawData, Type type, IEndPointContext context)
|
public object Deserialize(ReadOnlyMemory<byte> rawData, Type type, IEndPointContext context)
|
||||||
{
|
{
|
||||||
return null;
|
var reader = new CborReader(rawData);
|
||||||
|
return ReadData(reader, type, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
public T Cast<T>(object nonCasted, IEndPointContext context)
|
public T Cast<T>(object nonCasted, IEndPointContext context)
|
||||||
@@ -49,7 +58,7 @@ namespace mROA.Cbor
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void WriteData(object? obj, CborWriter writer)
|
private void WriteData(object? obj, CborWriter writer, IEndPointContext context)
|
||||||
{
|
{
|
||||||
switch (obj)
|
switch (obj)
|
||||||
{
|
{
|
||||||
@@ -65,9 +74,9 @@ namespace mROA.Cbor
|
|||||||
case double d:
|
case double d:
|
||||||
writer.WriteDouble(d);
|
writer.WriteDouble(d);
|
||||||
break;
|
break;
|
||||||
case decimal dec:
|
// case decimal dec:
|
||||||
writer.WriteDecimal(dec);
|
// writer.WriteDecimal(dec);
|
||||||
break;
|
// break;
|
||||||
case bool b:
|
case bool b:
|
||||||
writer.WriteBoolean(b);
|
writer.WriteBoolean(b);
|
||||||
break;
|
break;
|
||||||
@@ -90,35 +99,41 @@ namespace mROA.Cbor
|
|||||||
writer.WriteByteString(bytes);
|
writer.WriteByteString(bytes);
|
||||||
break;
|
break;
|
||||||
case IDictionary dictionary:
|
case IDictionary dictionary:
|
||||||
WriteDictionary(dictionary, writer);
|
WriteDictionary(dictionary, writer, context);
|
||||||
break;
|
break;
|
||||||
case IEnumerable enumerable:
|
case IList enumerable:
|
||||||
WriteEnumerable(enumerable, writer);
|
WriteList(enumerable, writer, context);
|
||||||
break;
|
break;
|
||||||
case SharedObject sharedObject:
|
case ISharedObject sharedObject:
|
||||||
|
sharedObject.EndPointContext = context;
|
||||||
|
WriteObject(sharedObject, writer, context);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
WriteObject(obj, writer);
|
if (obj.GetType().IsEnum)
|
||||||
|
{
|
||||||
|
writer.WriteUInt32((uint)obj);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
WriteObject(obj, writer, context);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void WriteEnumerable(IEnumerable enumerable, CborWriter writer)
|
private void WriteList(IList list, CborWriter writer, IEndPointContext context)
|
||||||
{
|
{
|
||||||
List<object?> list = new List<object?>();
|
|
||||||
|
|
||||||
foreach (var element in enumerable)
|
|
||||||
list.Add(element);
|
|
||||||
|
|
||||||
writer.WriteStartArray(list.Count);
|
writer.WriteStartArray(list.Count);
|
||||||
|
|
||||||
foreach (var element in list)
|
foreach (var element in list)
|
||||||
WriteData(element, writer);
|
WriteData(element, writer, context);
|
||||||
|
|
||||||
writer.WriteEndArray();
|
writer.WriteEndArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void WriteDictionary(IDictionary dictionary, CborWriter writer)
|
private void WriteDictionary(IDictionary dictionary, CborWriter writer, IEndPointContext context)
|
||||||
{
|
{
|
||||||
writer.WriteStartMap(dictionary.Count);
|
writer.WriteStartMap(dictionary.Count);
|
||||||
var keysEnumerator = dictionary.Keys.GetEnumerator();
|
var keysEnumerator = dictionary.Keys.GetEnumerator();
|
||||||
@@ -127,18 +142,158 @@ namespace mROA.Cbor
|
|||||||
{
|
{
|
||||||
keysEnumerator.MoveNext();
|
keysEnumerator.MoveNext();
|
||||||
valuesEnumerator.MoveNext();
|
valuesEnumerator.MoveNext();
|
||||||
WriteData(keysEnumerator.Current, writer);
|
WriteData(keysEnumerator.Current, writer, context);
|
||||||
WriteData(valuesEnumerator.Current, writer);
|
WriteData(valuesEnumerator.Current, writer, context);
|
||||||
}
|
|
||||||
writer.WriteEndMap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void WriteObject(object obj, CborWriter writer)
|
writer.WriteEndMap();
|
||||||
|
(keysEnumerator as IDisposable)?.Dispose();
|
||||||
|
(valuesEnumerator as IDisposable)?.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WriteObject(object obj, CborWriter writer, IEndPointContext context)
|
||||||
{
|
{
|
||||||
var type = obj.GetType();
|
var type = obj.GetType();
|
||||||
var properties = type.GetProperties();
|
var properties = FilterProperties(type.GetProperties());
|
||||||
var values = properties.Select(property => property.GetValue(obj));
|
var values = properties.Select(property => property.GetValue(obj)).ToList();
|
||||||
WriteEnumerable(values, writer);
|
WriteList(values, writer, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
private object ReadData(CborReader reader, Type? type, IEndPointContext context)
|
||||||
|
{
|
||||||
|
var state = reader.PeekState();
|
||||||
|
switch (state)
|
||||||
|
{
|
||||||
|
case CborReaderState.Boolean:
|
||||||
|
return reader.ReadBoolean();
|
||||||
|
case CborReaderState.UnsignedInteger:
|
||||||
|
case CborReaderState.NegativeInteger:
|
||||||
|
if (type == typeof(int))
|
||||||
|
return reader.ReadInt32();
|
||||||
|
if (type == typeof(long))
|
||||||
|
return reader.ReadInt64();
|
||||||
|
if (type == typeof(uint) || type.IsEnum)
|
||||||
|
return reader.ReadUInt32();
|
||||||
|
if (type == typeof(ulong))
|
||||||
|
return reader.ReadUInt64();
|
||||||
|
break;
|
||||||
|
case CborReaderState.ByteString:
|
||||||
|
return reader.ReadByteString();
|
||||||
|
case CborReaderState.TextString:
|
||||||
|
return reader.ReadTextString();
|
||||||
|
case CborReaderState.Null:
|
||||||
|
return null;
|
||||||
|
case CborReaderState.DoublePrecisionFloat:
|
||||||
|
return reader.ReadDouble();
|
||||||
|
case CborReaderState.SinglePrecisionFloat:
|
||||||
|
return reader.ReadSingle();
|
||||||
|
case CborReaderState.StartArray:
|
||||||
|
if (type == null)
|
||||||
|
return ReadList(reader, null, context);
|
||||||
|
|
||||||
|
if (type.IsSubclassOf(typeof(ISharedObject)))
|
||||||
|
return ReadSharedObject(reader, type, context);
|
||||||
|
|
||||||
|
if (type.IsSubclassOf(typeof(IList)))
|
||||||
|
return ReadList(reader, type, context);
|
||||||
|
|
||||||
|
return ReadObject(reader, type, context);
|
||||||
|
|
||||||
|
case CborReaderState.StartMap:
|
||||||
|
return ReadDictionary(reader, type, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (type == typeof(DateTimeOffset))
|
||||||
|
return reader.ReadDateTimeOffset();
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private Array ReadList(CborReader reader, Type? type, IEndPointContext context)
|
||||||
|
{
|
||||||
|
var length = reader.ReadStartArray();
|
||||||
|
if (length != null)
|
||||||
|
{
|
||||||
|
var values = new object[length.Value];
|
||||||
|
|
||||||
|
Type elementType = typeof(object);
|
||||||
|
|
||||||
|
if (type is { IsArray: true })
|
||||||
|
elementType = type.GetGenericArguments()[0];
|
||||||
|
|
||||||
|
|
||||||
|
for (int i = 0; i < length; i++)
|
||||||
|
{
|
||||||
|
values[i] = ReadData(reader, elementType, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Array.Empty<object>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private IDictionary ReadDictionary(CborReader reader, Type type, IEndPointContext context)
|
||||||
|
{
|
||||||
|
var dictionaryInstance = (Activator.CreateInstance(type) as IDictionary)!;
|
||||||
|
var length = reader.ReadStartArray();
|
||||||
|
if (length != null)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < length; i++)
|
||||||
|
{
|
||||||
|
var key = ReadData(reader, type, context);
|
||||||
|
var value = ReadData(reader, type, context);
|
||||||
|
dictionaryInstance.Add(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dictionaryInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
private object ReadObject(CborReader reader, Type type, IEndPointContext context)
|
||||||
|
{
|
||||||
|
var instance = Activator.CreateInstance(type)!;
|
||||||
|
|
||||||
|
FillObject(instance, type, reader, context);
|
||||||
|
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ISharedObject ReadSharedObject(CborReader reader, Type type, IEndPointContext context)
|
||||||
|
{
|
||||||
|
var sharedObject = (Activator.CreateInstance(type) as ISharedObject)!;
|
||||||
|
sharedObject.EndPointContext = context;
|
||||||
|
|
||||||
|
FillObject(sharedObject, type, reader, context);
|
||||||
|
|
||||||
|
return sharedObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void FillObject(object obj, Type type, CborReader reader, IEndPointContext context)
|
||||||
|
{
|
||||||
|
var properties = FilterProperties(type.GetProperties());
|
||||||
|
|
||||||
|
_ = reader.ReadStartArray();
|
||||||
|
|
||||||
|
foreach (var property in properties)
|
||||||
|
{
|
||||||
|
var value = ReadData(reader, property.PropertyType, context);
|
||||||
|
property.SetValue(obj, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
reader.ReadEndArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<PropertyInfo> FilterProperties(PropertyInfo[] properties)
|
||||||
|
{
|
||||||
|
var finalProperties = new List<PropertyInfo>(properties.Length);
|
||||||
|
foreach (var property in properties)
|
||||||
|
{
|
||||||
|
if (property.GetCustomAttribute<SerializationIgnoreAttribute>() == null)
|
||||||
|
finalProperties.Add(property);
|
||||||
|
}
|
||||||
|
|
||||||
|
return finalProperties;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5,12 +5,12 @@ namespace mROA.Cbor
|
|||||||
{
|
{
|
||||||
public interface IContextualSerializationToolKit
|
public interface IContextualSerializationToolKit
|
||||||
{
|
{
|
||||||
byte[] Serialize<T>(T objectToSerialize, IEndPointContext context);
|
byte[] Serialize(object objectToSerialize, IEndPointContext context);
|
||||||
byte[] Serialize(object objectToSerialize, Type type, IEndPointContext context);
|
void Serialize(object objectToSerialize, Span<byte> destination, IEndPointContext context);
|
||||||
T Deserialize<T>(byte[] rawData, IEndPointContext context);
|
T Deserialize<T>(byte[] rawData, IEndPointContext context);
|
||||||
object? Deserialize(byte[] rawData, Type type, IEndPointContext context);
|
object Deserialize(byte[] rawData, Type type, IEndPointContext context);
|
||||||
T Deserialize<T>(Span<byte> rawData, IEndPointContext context);
|
T Deserialize<T>(ReadOnlyMemory<byte> rawData, IEndPointContext context);
|
||||||
object? Deserialize(Span<byte> rawData, Type type, IEndPointContext context);
|
object Deserialize(ReadOnlyMemory<byte> rawData, Type type, IEndPointContext context);
|
||||||
T Cast<T>(object nonCasted, IEndPointContext context);
|
T Cast<T>(object nonCasted, IEndPointContext context);
|
||||||
object Cast(object nonCasted, Type type, IEndPointContext context);
|
object Cast(object nonCasted, Type type, IEndPointContext context);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,115 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using mROA.Cbor;
|
||||||
|
|
||||||
|
namespace mROA.Test;
|
||||||
|
|
||||||
|
public class CborTest
|
||||||
|
{
|
||||||
|
private ComplexTestObject _complexTestObject;
|
||||||
|
private IContextualSerializationToolKit _serializationToolKit;
|
||||||
|
private BasicCollectionElement _basicCollectionElement;
|
||||||
|
|
||||||
|
[SetUp]
|
||||||
|
public void Setup()
|
||||||
|
{
|
||||||
|
_complexTestObject = new ()
|
||||||
|
{
|
||||||
|
IntValue = 123,
|
||||||
|
DoubleValue = 3.14159,
|
||||||
|
StringValue = "abc",
|
||||||
|
CollectionElements =
|
||||||
|
[
|
||||||
|
_basicCollectionElement,
|
||||||
|
new BasicCollectionElement { A = 8_000_000, B = "Fi number", C = 1.618f }
|
||||||
|
],
|
||||||
|
IntArray = [1, 4, 8, 16, 87]
|
||||||
|
};
|
||||||
|
_basicCollectionElement = new() { A = 567565, B = "test text", C = 2.781f };
|
||||||
|
_serializationToolKit = new CborSerializaitonToolkit();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void BasicOnly()
|
||||||
|
{
|
||||||
|
var value = 123;
|
||||||
|
var data = _serializationToolKit.Serialize(value, null);
|
||||||
|
|
||||||
|
var deserialize = _serializationToolKit.Deserialize<int>(data, null);
|
||||||
|
Assert.That(value, Is.EqualTo(deserialize));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void ComplexFlat()
|
||||||
|
{
|
||||||
|
var value = _basicCollectionElement;
|
||||||
|
List<int> first = [1, 2, 3];
|
||||||
|
List<int> second = [1, 2, 3];
|
||||||
|
var eq = first.Equals(second);
|
||||||
|
var data = _serializationToolKit.Serialize(value, null);
|
||||||
|
var deserialize = _serializationToolKit.Deserialize<BasicCollectionElement>(data, null);
|
||||||
|
Assert.That(value, Is.EqualTo(deserialize));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void ComplexFull()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SharedObject()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private class ComplexTestObject
|
||||||
|
{
|
||||||
|
public int IntValue { get; set; }
|
||||||
|
public double DoubleValue { get; set; }
|
||||||
|
public string StringValue { get; set; }
|
||||||
|
public int[] IntArray { get; set; }
|
||||||
|
public BasicCollectionElement[] CollectionElements { get; set; }
|
||||||
|
|
||||||
|
protected bool Equals(ComplexTestObject other)
|
||||||
|
{
|
||||||
|
return IntValue == other.IntValue && DoubleValue.Equals(other.DoubleValue) && StringValue == other.StringValue && IntArray.Equals(other.IntArray) && CollectionElements.Equals(other.CollectionElements);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Equals(object? obj)
|
||||||
|
{
|
||||||
|
if (obj is null) return false;
|
||||||
|
if (ReferenceEquals(this, obj)) return true;
|
||||||
|
if (obj.GetType() != GetType()) return false;
|
||||||
|
return Equals((ComplexTestObject)obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
return HashCode.Combine(IntValue, DoubleValue, StringValue, IntArray, CollectionElements);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class BasicCollectionElement
|
||||||
|
{
|
||||||
|
public int A { get; set; }
|
||||||
|
public string B { get; set; }
|
||||||
|
public float C { get; set; }
|
||||||
|
|
||||||
|
protected bool Equals(BasicCollectionElement other)
|
||||||
|
{
|
||||||
|
return A == other.A && B == other.B && C.Equals(other.C);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Equals(object? obj)
|
||||||
|
{
|
||||||
|
if (obj is null) return false;
|
||||||
|
if (ReferenceEquals(this, obj)) return true;
|
||||||
|
if (obj.GetType() != GetType()) return false;
|
||||||
|
return Equals((BasicCollectionElement)obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
return HashCode.Combine(A, B, C);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -70,6 +70,7 @@ namespace mROA.Test
|
|||||||
[TearDown]
|
[TearDown]
|
||||||
public void TearDown()
|
public void TearDown()
|
||||||
{
|
{
|
||||||
|
_listener.Stop();
|
||||||
_listener.Dispose();
|
_listener.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netstandard2.1</TargetFramework>
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
<LangVersion>latest</LangVersion>
|
<LangVersion>latest</LangVersion>
|
||||||
|
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
@@ -27,6 +27,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\mROA.Cbor\mROA.Cbor.csproj" />
|
||||||
<ProjectReference Include="..\mROA\mROA.csproj" />
|
<ProjectReference Include="..\mROA\mROA.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace mROA.Implementation.Attributes
|
||||||
|
{
|
||||||
|
public class SerializationIgnoreAttribute : Attribute
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -32,11 +32,12 @@ namespace mROA.Implementation
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SharedObject : SharedObject<object>
|
public interface ISharedObject
|
||||||
{
|
{
|
||||||
|
IEndPointContext EndPointContext { get; set; }
|
||||||
}
|
}
|
||||||
public class SharedObject<T> where T : notnull
|
|
||||||
|
public class SharedObject<T> : ISharedObject where T : notnull
|
||||||
{
|
{
|
||||||
private IContextRepository GetDefaultContextRepository() =>
|
private IContextRepository GetDefaultContextRepository() =>
|
||||||
(OwnerId == TransmissionConfig.OwnershipRepository.GetHostOwnershipId()
|
(OwnerId == TransmissionConfig.OwnershipRepository.GetHostOwnershipId()
|
||||||
@@ -103,5 +104,7 @@ namespace mROA.Implementation
|
|||||||
|
|
||||||
public static implicit operator SharedObject<T>(T value) =>
|
public static implicit operator SharedObject<T>(T value) =>
|
||||||
new(value);
|
new(value);
|
||||||
|
|
||||||
|
public IEndPointContext EndPointContext { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user