diff --git a/Engine.Serialization/Converters/InstanceConverter.cs b/Engine.Serialization/Converters/EntityConverter.cs similarity index 69% rename from Engine.Serialization/Converters/InstanceConverter.cs rename to Engine.Serialization/Converters/EntityConverter.cs index 3ca4c72..1924436 100644 --- a/Engine.Serialization/Converters/InstanceConverter.cs +++ b/Engine.Serialization/Converters/EntityConverter.cs @@ -1,6 +1,6 @@ using System.Collections; using System.Reflection; - +using Syntriax.Engine.Core; using Syntriax.Engine.Core.Factory; using YamlDotNet.Core; @@ -9,17 +9,15 @@ using YamlDotNet.Serialization; namespace Syntriax.Engine.Serialization; -public class InstanceConverter : IEngineTypeYamlConverter +public class EntityConverter(IReadOnlyDictionary entities) : IYamlTypeConverter { - private int depth = 0; - private int maxDepth = 5; + private readonly IReadOnlyDictionary Entities = entities; + private readonly HashSet entitiesVisited = []; - public bool Accepts(Type type) => !type.IsPrimitive && type != typeof(string); + public bool Accepts(Type type) => typeof(IEntity).IsAssignableFrom(type); public object? ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeserializer) { - if (depth > maxDepth) - return null; parser.Consume(); TypeContainer typeContainer = (TypeContainer)rootDeserializer(typeof(TypeContainer))!; @@ -32,16 +30,12 @@ public class InstanceConverter : IEngineTypeYamlConverter if (type.GetField(key, BindingFlags.Instance | BindingFlags.NonPublic) is FieldInfo fieldInfo) { - depth++; object? fieldValue = rootDeserializer(fieldInfo.FieldType); - depth--; fieldInfo.SetValue(instance, fieldValue); } else if (type.GetProperty(key) is PropertyInfo propertyInfo) { - depth++; object? propertyValue = rootDeserializer(propertyInfo.PropertyType); - depth--; propertyInfo.SetValue(instance, propertyValue); } else @@ -55,6 +49,19 @@ public class InstanceConverter : IEngineTypeYamlConverter public void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer) { + if (type.HasAttribute()) + { + emitter.Emit(new Scalar("")); + return; + } + + IEntity entity = (IEntity)value!; + if (!entitiesVisited.Add(entity)) + { + emitter.Emit(new Scalar(entity.Id)); + return; + } + emitter.Emit(new MappingStart()); serializer(new TypeContainer(value), typeof(TypeContainer)); @@ -68,12 +75,7 @@ public class InstanceConverter : IEngineTypeYamlConverter emitter.Emit(new Scalar(propertyInfo.Name)); object? propertyValue = propertyInfo.GetValue(value); - depth++; - if (depth <= maxDepth) - EmitValue(propertyValue, propertyInfo.PropertyType, emitter, serializer); - else - emitter.Emit(new Scalar("Skipped")); - depth--; + EmitValue(propertyValue, propertyInfo.PropertyType, emitter, serializer); } foreach (FieldInfo fieldInfo in typeData.Fields) @@ -81,18 +83,16 @@ public class InstanceConverter : IEngineTypeYamlConverter if (fieldInfo.HasAttribute()) continue; - if (!fieldInfo.HasAttribute()) + if (fieldInfo.HasAttribute()) continue; + // if (!fieldInfo.HasAttribute()) + // continue; + emitter.Emit(new Scalar(fieldInfo.Name)); object? fieldValue = fieldInfo.GetValue(value); - depth++; - if (depth <= maxDepth) - EmitValue(fieldValue, fieldInfo.FieldType, emitter, serializer); - else - emitter.Emit(new Scalar("Skipped")); - depth--; + EmitValue(fieldValue, fieldInfo.FieldType, emitter, serializer); } emitter.Emit(new MappingEnd()); @@ -106,6 +106,12 @@ public class InstanceConverter : IEngineTypeYamlConverter return; } + if (value is IEntity entity) + { + emitter.Emit(new Scalar(entity.Id)); + return; + } + bool isSequence = Utils.IsEnumerable(declaredType); if (!isSequence) @@ -118,7 +124,10 @@ public class InstanceConverter : IEngineTypeYamlConverter emitter.Emit(new SequenceStart(null, null, false, SequenceStyle.Block)); foreach (object? item in sequence) - serializer(item); + if (value is IEntity sequenceEntity) + emitter.Emit(new Scalar(sequenceEntity.Id)); + else + serializer(item); emitter.Emit(new SequenceEnd()); } } diff --git a/Engine.Serialization/Converters/EntityFinder.cs b/Engine.Serialization/Converters/EntityFinder.cs index 4349ff6..4a173f4 100644 --- a/Engine.Serialization/Converters/EntityFinder.cs +++ b/Engine.Serialization/Converters/EntityFinder.cs @@ -7,9 +7,9 @@ namespace Syntriax.Engine.Serialization; public class EntityFinder { - private readonly HashSet _entities = []; + private readonly Dictionary _entities = []; - public IReadOnlyCollection Entities => _entities; + public IReadOnlyDictionary Entities => _entities; public void FindEntitiesUnder(object @object) { @@ -23,9 +23,11 @@ public class EntityFinder return; } - if (!_entities.Add(entity)) + if (Entities.ContainsKey(entity.Id)) return; + _entities.Add(entity.Id, entity); + foreach (PropertyInfo propertyInfo in typeData.Properties) { if (propertyInfo.PropertyType?.IsPrimitive ?? true || propertyInfo.PropertyType == typeof(string)) diff --git a/Engine.Serialization/IgnoreSerializationAttribute.cs b/Engine.Serialization/IgnoreSerializationAttribute.cs index 1b8499a..f182578 100644 --- a/Engine.Serialization/IgnoreSerializationAttribute.cs +++ b/Engine.Serialization/IgnoreSerializationAttribute.cs @@ -1,4 +1,4 @@ namespace Syntriax.Engine.Serialization; -[AttributeUsage(AttributeTargets.Property | AttributeTargets.Class)] +[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Class)] public class IgnoreSerializationAttribute : Attribute; diff --git a/Engine.Serialization/Serializer.cs b/Engine.Serialization/Serializer.cs index 6e6cb3d..085d9cb 100644 --- a/Engine.Serialization/Serializer.cs +++ b/Engine.Serialization/Serializer.cs @@ -7,7 +7,7 @@ namespace Syntriax.Engine.Serialization; public static class Serializer { private static readonly ISerializer serializer = GetSerializer(); - private static ISerializer GetSerializer() + private static ISerializer GetSerializer(EntityFinder? entityFinder = null) { SerializerBuilder serializerBuilder = new SerializerBuilder() .WithNamingConvention(PascalCaseNamingConvention.Instance) @@ -16,6 +16,9 @@ public static class Serializer foreach (IEngineTypeYamlConverter typeConverter in GetEngineYamlTypeConverters()) serializerBuilder = serializerBuilder.WithTypeConverter(typeConverter); + if (entityFinder is not null) + serializerBuilder = serializerBuilder.WithTypeConverter(new EntityConverter(entityFinder.Entities)); + return serializerBuilder.Build(); } @@ -39,7 +42,10 @@ public static class Serializer public static string Serialize(object instance) { - return serializer.Serialize(instance); + EntityFinder entityFinder = new(); + entityFinder.FindEntitiesUnder(instance); + ISerializer serializer = GetSerializer(entityFinder); + return serializer.Serialize(entityFinder.Entities); } public static object Deserialize(string yaml) diff --git a/Engine.Serialization/Utils.cs b/Engine.Serialization/Utils.cs index 65ef01c..79f7c8f 100644 --- a/Engine.Serialization/Utils.cs +++ b/Engine.Serialization/Utils.cs @@ -9,10 +9,15 @@ internal static class Utils internal static TypeData GetTypeData(this Type objectType) { - List fieldInfos = GetFieldInfosIncludingBaseClasses(objectType, BindingFlags.Instance | BindingFlags.FlattenHierarchy | BindingFlags.NonPublic | BindingFlags.Public); + List eventInfos = objectType.GetEvents(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public) + .OrderBy(ei => ei.Name) + .ToList(); + + List fieldInfos = GetFieldInfosIncludingBaseClasses(objectType, BindingFlags.Instance | BindingFlags.FlattenHierarchy | BindingFlags.NonPublic | BindingFlags.Public) + .Where(pi => !eventInfos.Any(ei => pi.Name.CompareTo(ei.Name) == 0)).ToList(); List propertyInfos = GetPropertyInfosIncludingBaseClasses(objectType, BindingFlags.Instance | BindingFlags.FlattenHierarchy | BindingFlags.Public) - .Where(pi => pi.SetMethod is not null) + .Where(pi => pi.SetMethod is not null && !eventInfos.Any(ei => pi.Name.CompareTo(ei.Name) == 0)) .ToList(); return new TypeData(fieldInfos, propertyInfos);