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); } }