Cbor часть 2, десериализация

This commit is contained in:
2025-02-25 08:43:01 +03:00
parent 05ee651e89
commit c0bb3c523a
7 changed files with 322 additions and 38 deletions
+186 -31
View File
@@ -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 -5
View File
@@ -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);
} }
+115
View File
@@ -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);
}
}
}
+1
View File
@@ -70,6 +70,7 @@ namespace mROA.Test
[TearDown] [TearDown]
public void TearDown() public void TearDown()
{ {
_listener.Stop();
_listener.Dispose(); _listener.Dispose();
} }
} }
+2 -1
View File
@@ -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
{
}
}
+6 -3
View File
@@ -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; }
} }
} }