From eb454a471c2a9ea04f474dabfea77ee131028b72 Mon Sep 17 00:00:00 2001 From: Syntriax Date: Thu, 17 Apr 2025 22:21:12 +0300 Subject: [PATCH] feat: added primitive serialization --- .../Converters/AABBConverter.cs | 41 +++++ .../Abstract/IEngineTypeYamlConverter.cs | 5 + .../Converters/CircleConverter.cs | 41 +++++ .../Converters/Line2DConverter.cs | 41 +++++ .../Converters/Line2DEquationConverter.cs | 41 +++++ .../Converters/Projection1DConverter.cs | 41 +++++ .../Converters/QuaternionConverter.cs | 28 +++ .../Converters/Shape2DConverter.cs | 47 +++++ .../Converters/TriangleConverter.cs | 47 +++++ .../Converters/Vector2DConverter.cs | 28 +++ .../Converters/Vector3DConverter.cs | 28 +++ Engine.Serialization/EntityFieldConverter.cs | 172 ------------------ Engine.Serialization/EntityReference.cs | 6 - Engine.Serialization/Serializer.cs | 46 +++-- Engine.Serialization/Utils.cs | 31 ++++ 15 files changed, 454 insertions(+), 189 deletions(-) create mode 100644 Engine.Serialization/Converters/AABBConverter.cs create mode 100644 Engine.Serialization/Converters/Abstract/IEngineTypeYamlConverter.cs create mode 100644 Engine.Serialization/Converters/CircleConverter.cs create mode 100644 Engine.Serialization/Converters/Line2DConverter.cs create mode 100644 Engine.Serialization/Converters/Line2DEquationConverter.cs create mode 100644 Engine.Serialization/Converters/Projection1DConverter.cs create mode 100644 Engine.Serialization/Converters/QuaternionConverter.cs create mode 100644 Engine.Serialization/Converters/Shape2DConverter.cs create mode 100644 Engine.Serialization/Converters/TriangleConverter.cs create mode 100644 Engine.Serialization/Converters/Vector2DConverter.cs create mode 100644 Engine.Serialization/Converters/Vector3DConverter.cs delete mode 100644 Engine.Serialization/EntityFieldConverter.cs delete mode 100644 Engine.Serialization/EntityReference.cs create mode 100644 Engine.Serialization/Utils.cs diff --git a/Engine.Serialization/Converters/AABBConverter.cs b/Engine.Serialization/Converters/AABBConverter.cs new file mode 100644 index 0000000..f013fba --- /dev/null +++ b/Engine.Serialization/Converters/AABBConverter.cs @@ -0,0 +1,41 @@ +using Syntriax.Engine.Core; + +using YamlDotNet.Core; +using YamlDotNet.Core.Events; +using YamlDotNet.Serialization; + +namespace Syntriax.Engine.Serialization; + +public class AABBConverter : IEngineTypeYamlConverter +{ + public bool Accepts(Type type) => type == typeof(AABB); + + public object? ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeserializer) + { + parser.Consume(); + + if (parser.Consume().Value.CompareTo(nameof(AABB.LowerBoundary)) != 0) + throw new ArgumentException($"{nameof(AABB)} mapping must start with {nameof(AABB.LowerBoundary)}"); + Vector2D lowerBoundary = (Vector2D)rootDeserializer(typeof(Vector2D))!; + + if (parser.Consume().Value.CompareTo(nameof(AABB.UpperBoundary)) != 0) + throw new ArgumentException($"{nameof(AABB)} mapping must end with {nameof(AABB.UpperBoundary)}"); + Vector2D upperBoundary = (Vector2D)rootDeserializer(typeof(Vector2D))!; + + parser.Consume(); + + return new AABB(lowerBoundary, upperBoundary); + } + + public void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer) + { + AABB aabb = (AABB)value!; + + emitter.Emit(new MappingStart()); + emitter.Emit(new Scalar(nameof(AABB.LowerBoundary))); + serializer(aabb.LowerBoundary, typeof(Vector2D)); + emitter.Emit(new Scalar(nameof(AABB.UpperBoundary))); + serializer(aabb.UpperBoundary, typeof(Vector2D)); + emitter.Emit(new MappingEnd()); + } +} diff --git a/Engine.Serialization/Converters/Abstract/IEngineTypeYamlConverter.cs b/Engine.Serialization/Converters/Abstract/IEngineTypeYamlConverter.cs new file mode 100644 index 0000000..f260755 --- /dev/null +++ b/Engine.Serialization/Converters/Abstract/IEngineTypeYamlConverter.cs @@ -0,0 +1,5 @@ +using YamlDotNet.Serialization; + +namespace Syntriax.Engine.Serialization; + +public interface IEngineTypeYamlConverter : IYamlTypeConverter; diff --git a/Engine.Serialization/Converters/CircleConverter.cs b/Engine.Serialization/Converters/CircleConverter.cs new file mode 100644 index 0000000..c2d1a3e --- /dev/null +++ b/Engine.Serialization/Converters/CircleConverter.cs @@ -0,0 +1,41 @@ +using Syntriax.Engine.Core; + +using YamlDotNet.Core; +using YamlDotNet.Core.Events; +using YamlDotNet.Serialization; + +namespace Syntriax.Engine.Serialization; + +public class CircleConverter : IEngineTypeYamlConverter +{ + public bool Accepts(Type type) => type == typeof(Circle); + + public object? ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeserializer) + { + parser.Consume(); + + if (parser.Consume().Value.CompareTo(nameof(Circle.Center)) != 0) + throw new ArgumentException($"{nameof(Circle)} mapping must start with {nameof(Circle.Center)}"); + Vector2D lowerBoundary = (Vector2D)rootDeserializer(typeof(Vector2D))!; + + if (parser.Consume().Value.CompareTo(nameof(Circle.Radius)) != 0) + throw new ArgumentException($"{nameof(Circle)} mapping must end with {nameof(Circle.Radius)}"); + float radius = (float)rootDeserializer(typeof(float))!; + + parser.Consume(); + + return new Circle(lowerBoundary, radius); + } + + public void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer) + { + Circle circle = (Circle)value!; + + emitter.Emit(new MappingStart()); + emitter.Emit(new Scalar(nameof(Circle.Center))); + serializer(circle.Center, typeof(Vector2D)); + emitter.Emit(new Scalar(nameof(Circle.Radius))); + serializer(circle.Radius, typeof(float)); + emitter.Emit(new MappingEnd()); + } +} diff --git a/Engine.Serialization/Converters/Line2DConverter.cs b/Engine.Serialization/Converters/Line2DConverter.cs new file mode 100644 index 0000000..1da6495 --- /dev/null +++ b/Engine.Serialization/Converters/Line2DConverter.cs @@ -0,0 +1,41 @@ +using Syntriax.Engine.Core; + +using YamlDotNet.Core; +using YamlDotNet.Core.Events; +using YamlDotNet.Serialization; + +namespace Syntriax.Engine.Serialization; + +public class Line2DConverter : IEngineTypeYamlConverter +{ + public bool Accepts(Type type) => type == typeof(Line2D); + + public object? ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeserializer) + { + parser.Consume(); + + if (parser.Consume().Value.CompareTo(nameof(Line2D.From)) != 0) + throw new ArgumentException($"{nameof(Line2D)} mapping must start with {nameof(Line2D.From)}"); + Vector2D from = (Vector2D)rootDeserializer(typeof(Vector2D))!; + + if (parser.Consume().Value.CompareTo(nameof(Line2D.To)) != 0) + throw new ArgumentException($"{nameof(Line2D)} mapping must end with {nameof(Line2D.To)}"); + Vector2D to = (Vector2D)rootDeserializer(typeof(Vector2D))!; + + parser.Consume(); + + return new Line2D(from, to); + } + + public void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer) + { + Line2D line2D = (Line2D)value!; + + emitter.Emit(new MappingStart()); + emitter.Emit(new Scalar(nameof(Line2D.From))); + serializer(line2D.From, typeof(Vector2D)); + emitter.Emit(new Scalar(nameof(Line2D.To))); + serializer(line2D.To, typeof(Vector2D)); + emitter.Emit(new MappingEnd()); + } +} diff --git a/Engine.Serialization/Converters/Line2DEquationConverter.cs b/Engine.Serialization/Converters/Line2DEquationConverter.cs new file mode 100644 index 0000000..b982666 --- /dev/null +++ b/Engine.Serialization/Converters/Line2DEquationConverter.cs @@ -0,0 +1,41 @@ +using Syntriax.Engine.Core; + +using YamlDotNet.Core; +using YamlDotNet.Core.Events; +using YamlDotNet.Serialization; + +namespace Syntriax.Engine.Serialization; + +public class Line2DEquationConverter : IEngineTypeYamlConverter +{ + public bool Accepts(Type type) => type == typeof(Line2DEquation); + + public object? ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeserializer) + { + parser.Consume(); + + if (parser.Consume().Value.CompareTo(nameof(Line2DEquation.Slope)) != 0) + throw new ArgumentException($"{nameof(Line2DEquation)} mapping must start with {nameof(Line2DEquation.Slope)}"); + float slope = (float)rootDeserializer(typeof(float))!; + + if (parser.Consume().Value.CompareTo(nameof(Line2DEquation.OffsetY)) != 0) + throw new ArgumentException($"{nameof(Line2DEquation)} mapping must end with {nameof(Line2DEquation.OffsetY)}"); + float offset = (float)rootDeserializer(typeof(float))!; + + parser.Consume(); + + return new Line2DEquation(slope, offset); + } + + public void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer) + { + Line2DEquation line2DEquation = (Line2DEquation)value!; + + emitter.Emit(new MappingStart()); + emitter.Emit(new Scalar(nameof(Line2DEquation.Slope))); + serializer(line2DEquation.Slope, typeof(float)); + emitter.Emit(new Scalar(nameof(Line2DEquation.OffsetY))); + serializer(line2DEquation.OffsetY, typeof(float)); + emitter.Emit(new MappingEnd()); + } +} diff --git a/Engine.Serialization/Converters/Projection1DConverter.cs b/Engine.Serialization/Converters/Projection1DConverter.cs new file mode 100644 index 0000000..f2bb88c --- /dev/null +++ b/Engine.Serialization/Converters/Projection1DConverter.cs @@ -0,0 +1,41 @@ +using Syntriax.Engine.Core; + +using YamlDotNet.Core; +using YamlDotNet.Core.Events; +using YamlDotNet.Serialization; + +namespace Syntriax.Engine.Serialization; + +public class Projection1DConverter : IEngineTypeYamlConverter +{ + public bool Accepts(Type type) => type == typeof(Projection1D); + + public object? ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeserializer) + { + parser.Consume(); + + if (parser.Consume().Value.CompareTo(nameof(Projection1D.Min)) != 0) + throw new ArgumentException($"{nameof(Projection1D)} mapping must start with {nameof(Projection1D.Min)}"); + float min = (float)rootDeserializer(typeof(float))!; + + if (parser.Consume().Value.CompareTo(nameof(Projection1D.Max)) != 0) + throw new ArgumentException($"{nameof(Projection1D)} mapping must end with {nameof(Projection1D.Max)}"); + float max = (float)rootDeserializer(typeof(float))!; + + parser.Consume(); + + return new Projection1D(min, max); + } + + public void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer) + { + Projection1D projection1D = (Projection1D)value!; + + emitter.Emit(new MappingStart()); + emitter.Emit(new Scalar(nameof(Projection1D.Min))); + serializer(projection1D.Min, typeof(float)); + emitter.Emit(new Scalar(nameof(Projection1D.Max))); + serializer(projection1D.Max, typeof(float)); + emitter.Emit(new MappingEnd()); + } +} diff --git a/Engine.Serialization/Converters/QuaternionConverter.cs b/Engine.Serialization/Converters/QuaternionConverter.cs new file mode 100644 index 0000000..8552ee7 --- /dev/null +++ b/Engine.Serialization/Converters/QuaternionConverter.cs @@ -0,0 +1,28 @@ +using Syntriax.Engine.Core; + +using YamlDotNet.Core; +using YamlDotNet.Core.Events; +using YamlDotNet.Serialization; + +namespace Syntriax.Engine.Serialization; + +public class QuaternionConverter : IEngineTypeYamlConverter +{ + private static readonly int SUBSTRING_START_LENGTH = nameof(Quaternion).Length + 1; + + public bool Accepts(Type type) => type == typeof(Quaternion); + + public object? ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeserializer) + { + string value = parser.Consume().Value; + string insideParenthesis = value[SUBSTRING_START_LENGTH..^1]; + string[] values = insideParenthesis.Split(", "); + return new Quaternion(float.Parse(values[0]), float.Parse(values[1]), float.Parse(values[2]), float.Parse(values[3])); + } + + public void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer) + { + Quaternion quaternion = (Quaternion)value!; + emitter.Emit(new Scalar($"{nameof(Quaternion)}({quaternion.X}, {quaternion.Y}, {quaternion.Z}, {quaternion.W})")); + } +} diff --git a/Engine.Serialization/Converters/Shape2DConverter.cs b/Engine.Serialization/Converters/Shape2DConverter.cs new file mode 100644 index 0000000..4c7f2f1 --- /dev/null +++ b/Engine.Serialization/Converters/Shape2DConverter.cs @@ -0,0 +1,47 @@ +using Syntriax.Engine.Core; + +using YamlDotNet.Core; +using YamlDotNet.Core.Events; +using YamlDotNet.Serialization; + +namespace Syntriax.Engine.Serialization; + +public class Shape2DConverter : IEngineTypeYamlConverter +{ + public bool Accepts(Type type) => type == typeof(Shape2D); + + public object? ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeserializer) + { + parser.Consume(); + + if (parser.Consume().Value.CompareTo(nameof(Shape2D.Vertices)) != 0) + throw new ArgumentException($"{nameof(Shape2D)} mapping have a {nameof(Shape2D.Vertices)}"); + + parser.Consume(); + + List vertices = []; + + while (!parser.TryConsume(out _)) + { + Vector2D vertex = (Vector2D)rootDeserializer(typeof(Vector2D))!; + vertices.Add(vertex); + } + + parser.Consume(); + + return new Shape2D(vertices); + } + + public void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer) + { + Shape2D shape2D = (Shape2D)value!; + + emitter.Emit(new MappingStart()); + emitter.Emit(new Scalar(nameof(Shape2D.Vertices))); + emitter.Emit(new SequenceStart(anchor: null, tag: null, isImplicit: false, style: SequenceStyle.Block)); + foreach (Vector2D vertex in shape2D.Vertices) + serializer(vertex, typeof(Vector2D)); + emitter.Emit(new SequenceEnd()); + emitter.Emit(new MappingEnd()); + } +} diff --git a/Engine.Serialization/Converters/TriangleConverter.cs b/Engine.Serialization/Converters/TriangleConverter.cs new file mode 100644 index 0000000..e989905 --- /dev/null +++ b/Engine.Serialization/Converters/TriangleConverter.cs @@ -0,0 +1,47 @@ +using Syntriax.Engine.Core; + +using YamlDotNet.Core; +using YamlDotNet.Core.Events; +using YamlDotNet.Serialization; + +namespace Syntriax.Engine.Serialization; + +public class TriangleConverter : IEngineTypeYamlConverter +{ + public bool Accepts(Type type) => type == typeof(Triangle); + + public object? ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeserializer) + { + parser.Consume(); + + if (parser.Consume().Value.CompareTo(nameof(Triangle.A)) != 0) + throw new ArgumentException($"{nameof(Triangle)} mapping must start with {nameof(Triangle.A)}"); + Vector2D a = (Vector2D)rootDeserializer(typeof(Vector2D))!; + + if (parser.Consume().Value.CompareTo(nameof(Triangle.B)) != 0) + throw new ArgumentException($"{nameof(Triangle)} mapping must have {nameof(Triangle.B)} after {nameof(Triangle.A)}"); + Vector2D b = (Vector2D)rootDeserializer(typeof(Vector2D))!; + + if (parser.Consume().Value.CompareTo(nameof(Triangle.C)) != 0) + throw new ArgumentException($"{nameof(Triangle)} mapping must end with {nameof(Triangle.C)}"); + Vector2D c = (Vector2D)rootDeserializer(typeof(Vector2D))!; + + parser.Consume(); + + return new Triangle(a, b, c); + } + + public void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer) + { + Triangle aabb = (Triangle)value!; + + emitter.Emit(new MappingStart()); + emitter.Emit(new Scalar(nameof(Triangle.A))); + serializer(aabb.A, typeof(Vector2D)); + emitter.Emit(new Scalar(nameof(Triangle.B))); + serializer(aabb.B, typeof(Vector2D)); + emitter.Emit(new Scalar(nameof(Triangle.C))); + serializer(aabb.C, typeof(Vector2D)); + emitter.Emit(new MappingEnd()); + } +} diff --git a/Engine.Serialization/Converters/Vector2DConverter.cs b/Engine.Serialization/Converters/Vector2DConverter.cs new file mode 100644 index 0000000..fde0561 --- /dev/null +++ b/Engine.Serialization/Converters/Vector2DConverter.cs @@ -0,0 +1,28 @@ +using Syntriax.Engine.Core; + +using YamlDotNet.Core; +using YamlDotNet.Core.Events; +using YamlDotNet.Serialization; + +namespace Syntriax.Engine.Serialization; + +public class Vector2DConverter : IEngineTypeYamlConverter +{ + private static readonly int SUBSTRING_START_LENGTH = nameof(Vector2D).Length + 1; + + public bool Accepts(Type type) => type == typeof(Vector2D); + + public object? ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeserializer) + { + string value = parser.Consume().Value; + string insideParenthesis = value[SUBSTRING_START_LENGTH..^1]; + string[] values = insideParenthesis.Split(", "); + return new Vector2D(float.Parse(values[0]), float.Parse(values[1])); + } + + public void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer) + { + Vector2D vector2D = (Vector2D)value!; + emitter.Emit(new Scalar($"{nameof(Vector2D)}({vector2D.X}, {vector2D.Y})")); + } +} diff --git a/Engine.Serialization/Converters/Vector3DConverter.cs b/Engine.Serialization/Converters/Vector3DConverter.cs new file mode 100644 index 0000000..3970625 --- /dev/null +++ b/Engine.Serialization/Converters/Vector3DConverter.cs @@ -0,0 +1,28 @@ +using Syntriax.Engine.Core; + +using YamlDotNet.Core; +using YamlDotNet.Core.Events; +using YamlDotNet.Serialization; + +namespace Syntriax.Engine.Serialization; + +public class Vector3DConverter : IEngineTypeYamlConverter +{ + private static readonly int SUBSTRING_START_LENGTH = nameof(Vector3D).Length + 1; + + public bool Accepts(Type type) => type == typeof(Vector3D); + + public object? ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeserializer) + { + string value = parser.Consume().Value; + string insideParenthesis = value[SUBSTRING_START_LENGTH..^1]; + string[] values = insideParenthesis.Split(", "); + return new Vector3D(float.Parse(values[0]), float.Parse(values[1]), float.Parse(values[2])); + } + + public void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer) + { + Vector3D vector3D = (Vector3D)value!; + emitter.Emit(new Scalar($"{nameof(Vector3D)}({vector3D.X}, {vector3D.Y}, {vector3D.Z})")); + } +} diff --git a/Engine.Serialization/EntityFieldConverter.cs b/Engine.Serialization/EntityFieldConverter.cs deleted file mode 100644 index 028edb5..0000000 --- a/Engine.Serialization/EntityFieldConverter.cs +++ /dev/null @@ -1,172 +0,0 @@ -using System.Collections; -using System.Reflection; -using Syntriax.Engine.Core; - -using YamlDotNet.Core; -using YamlDotNet.Core.Events; -using YamlDotNet.Serialization; -using YamlDotNet.Serialization.NamingConventions; - -namespace Syntriax.Engine.Serialization; - -public class Vector2DConverter : IYamlTypeConverter -{ - public bool Accepts(Type type) - { - return type == typeof(Vector2D); - } - - public object? ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeserializer) - { - parser.Consume(); - - float x = 0f; - float y = 0f; - - while (!parser.TryConsume(out _)) - { - string key = parser.Consume().Value; - string value = parser.Consume().Value; - - if (key.CompareTo(nameof(Vector2D.X)) == 0) x = float.Parse(value); - if (key.CompareTo(nameof(Vector2D.Y)) == 0) y = float.Parse(value); - } - - return new Vector2D(x, y); - } - - public void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer) - { - Vector2D vector2D = (Vector2D)value!; - - emitter.Emit(new MappingStart()); - emitter.Emit(new Scalar(nameof(Vector2D.X))); - emitter.Emit(new Scalar(vector2D.X.ToString())); - emitter.Emit(new Scalar(nameof(Vector2D.Y))); - emitter.Emit(new Scalar(vector2D.Y.ToString())); - emitter.Emit(new MappingEnd()); - } -} - -public class EntityConverter : IYamlTypeConverter -{ - private static readonly ISerializer serializer = new SerializerBuilder() - .WithNamingConvention(PascalCaseNamingConvention.Instance) - .DisableAliases() - .Build(); - - private static readonly IDeserializer deserializer = new DeserializerBuilder() - .WithNamingConvention(PascalCaseNamingConvention.Instance) - .Build(); - - public bool Accepts(Type type) - { - bool v = typeof(IEntity).IsAssignableFrom(type); - return v; - } - - public object? ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeserializer) - { - string? id = null; - - parser.Consume(); - - while (!parser.TryConsume(out _)) - { - string key = parser.Consume().Value; - string value = parser.Consume().Value; - - if (key.CompareTo(nameof(EntityReference.Id)) == 0) - id = value; - } - - return new EntityReference() { Id = id }; - } - public void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer) - { - if (value is not IEntity entity) - return; - - var typeData = GetTypeData(type); - emitter.Emit(new MappingStart()); - - foreach (var fieldInfo in typeData.Fields) - { - if (fieldInfo.GetCustomAttribute() != null) - continue; - - emitter.Emit(new Scalar(fieldInfo.Name)); - var fieldValue = fieldInfo.GetValue(entity); - - EmitValue(fieldValue, fieldInfo.FieldType, emitter, serializer); - } - - foreach (var propertyInfo in typeData.Properties) - { - emitter.Emit(new Scalar(propertyInfo.Name)); - var propValue = propertyInfo.GetValue(entity); - - EmitValue(propValue, propertyInfo.PropertyType, emitter, serializer); - } - - emitter.Emit(new MappingEnd()); - } - private void EmitValue(object? value, Type declaredType, IEmitter emitter, ObjectSerializer serializer) - { - if (value is null) - { - emitter.Emit(new Scalar("null")); - return; - } - - if (value is IEntity singleEntity && !IsEnumerable(declaredType)) - { - emitter.Emit(new Scalar(singleEntity.Id)); - return; - } - - if (IsEnumerable(declaredType) && value is System.Collections.IEnumerable list) - { - emitter.Emit(new SequenceStart(null, null, false, SequenceStyle.Block)); - foreach (var item in list) - { - if (item is IEntity itemEntity) - emitter.Emit(new Scalar(itemEntity.Id)); - else - serializer(item); // fallback for non-entity items - } - emitter.Emit(new SequenceEnd()); - return; - } - - // fallback - serializer(value); - } - private static TypeData GetTypeData(Type objectType) - { - IEnumerable eventInfos = objectType.GetEvents(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public) - .OrderBy(ei => ei.Name) - .AsEnumerable(); - IEnumerable fieldInfos = objectType.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public) - .Where(fi => !eventInfos.Any(ei => fi.Name.CompareTo(ei.Name) == 0)) - .OrderBy(ei => ei.Name) //ei => ei.FieldType.IsPrimitive || ei.FieldType == typeof(string)) - // .ThenByDescending(ei => ei.Name) - .AsEnumerable(); - IEnumerable propertyInfos = objectType.GetProperties(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public) - .Where(pi => pi.SetMethod is not null) - .OrderBy(ei => ei.Name)// ei => ei.PropertyType.IsPrimitive || ei.PropertyType == typeof(string)) - // .ThenByDescending(ei => ei.Name) - .AsEnumerable(); - - return new TypeData(eventInfos, fieldInfos, propertyInfos); - } - private static bool IsEnumerable(Type type) - { - return typeof(System.Collections.IEnumerable).IsAssignableFrom(type) && type != typeof(string); - } - private record struct TypeData(IEnumerable Events, IEnumerable Fields, IEnumerable Properties) - { - public static implicit operator (IEnumerable events, IEnumerable fields, IEnumerable properties)(TypeData value) => (value.Events, value.Fields, value.Properties); - public static implicit operator TypeData((IEnumerable events, IEnumerable fields, IEnumerable properties) value) => new TypeData(value.events, value.fields, value.properties); - } -} diff --git a/Engine.Serialization/EntityReference.cs b/Engine.Serialization/EntityReference.cs deleted file mode 100644 index 218847d..0000000 --- a/Engine.Serialization/EntityReference.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Syntriax.Engine.Serialization; - -public class EntityReference -{ - public string Id { get; set; } -} diff --git a/Engine.Serialization/Serializer.cs b/Engine.Serialization/Serializer.cs index c96647f..6e6cb3d 100644 --- a/Engine.Serialization/Serializer.cs +++ b/Engine.Serialization/Serializer.cs @@ -1,3 +1,4 @@ +using System.Reflection; using YamlDotNet.Serialization; using YamlDotNet.Serialization.NamingConventions; @@ -5,24 +6,47 @@ namespace Syntriax.Engine.Serialization; public static class Serializer { - private static readonly ISerializer serializer = new SerializerBuilder() - .WithNamingConvention(PascalCaseNamingConvention.Instance) - .DisableAliases() - .WithTypeConverter(new EntityConverter()) - .WithTypeConverter(new Vector2DConverter()) - .Build(); + private static readonly ISerializer serializer = GetSerializer(); + private static ISerializer GetSerializer() + { + SerializerBuilder serializerBuilder = new SerializerBuilder() + .WithNamingConvention(PascalCaseNamingConvention.Instance) + .DisableAliases(); - private static readonly IDeserializer deserializer = new DeserializerBuilder() - .WithNamingConvention(PascalCaseNamingConvention.Instance) - .WithTypeConverter(new EntityConverter()) - .WithTypeConverter(new Vector2DConverter()) - .Build(); + foreach (IEngineTypeYamlConverter typeConverter in GetEngineYamlTypeConverters()) + serializerBuilder = serializerBuilder.WithTypeConverter(typeConverter); + + return serializerBuilder.Build(); + } + + private static readonly IDeserializer deserializer = GetDeserializer(); + private static IDeserializer GetDeserializer() + { + DeserializerBuilder serializerBuilder = new DeserializerBuilder() + .WithNamingConvention(PascalCaseNamingConvention.Instance); + + foreach (IEngineTypeYamlConverter typeConverter in GetEngineYamlTypeConverters()) + serializerBuilder = serializerBuilder.WithTypeConverter(typeConverter); + + return serializerBuilder.Build(); + } + + private static IEnumerable GetEngineYamlTypeConverters() + { + foreach (Type type in Assembly.GetExecutingAssembly().GetTypes().Where(t => typeof(IEngineTypeYamlConverter).IsAssignableFrom(t) && t.IsClass && !t.IsAbstract)) + yield return (Activator.CreateInstance(type) as IEngineTypeYamlConverter)!; + } public static string Serialize(object instance) { return serializer.Serialize(instance); } + public static object Deserialize(string yaml) + { + return deserializer.Deserialize(yaml)!; + } + public static T Deserialize(string yaml) { return deserializer.Deserialize(yaml); diff --git a/Engine.Serialization/Utils.cs b/Engine.Serialization/Utils.cs new file mode 100644 index 0000000..fa14510 --- /dev/null +++ b/Engine.Serialization/Utils.cs @@ -0,0 +1,31 @@ +using System.Reflection; + +namespace Syntriax.Engine.Serialization; + +internal static class Utils +{ + internal static TypeData GetTypeData(this Type type) + { + IEnumerable eventInfos = type.GetEvents(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public) + .OrderBy(ei => ei.Name) + .AsEnumerable(); + IEnumerable fieldInfos = type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public) + .Where(fi => !eventInfos.Any(ei => fi.Name.CompareTo(ei.Name) == 0)) + .OrderBy(ei => ei.Name) //ei => ei.FieldType.IsPrimitive || ei.FieldType == typeof(string)) + // .ThenByDescending(ei => ei.Name) + .AsEnumerable(); + IEnumerable propertyInfos = type.GetProperties(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public) + .Where(pi => pi.SetMethod is not null) + .OrderBy(ei => ei.Name)// ei => ei.PropertyType.IsPrimitive || ei.PropertyType == typeof(string)) + // .ThenByDescending(ei => ei.Name) + .AsEnumerable(); + + return new TypeData(eventInfos, fieldInfos, propertyInfos); + } +} + +internal record struct TypeData(IEnumerable Events, IEnumerable Fields, IEnumerable Properties) +{ + public static implicit operator (IEnumerable events, IEnumerable fields, IEnumerable properties)(TypeData value) => (value.Events, value.Fields, value.Properties); + public static implicit operator TypeData((IEnumerable events, IEnumerable fields, IEnumerable properties) value) => new TypeData(value.events, value.fields, value.properties); +}