Syntriax.Engine/Engine.Serialization/EntityFieldConverter.cs

173 lines
6.3 KiB
C#

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<MappingStart>();
float x = 0f;
float y = 0f;
while (!parser.TryConsume<MappingEnd>(out _))
{
string key = parser.Consume<Scalar>().Value;
string value = parser.Consume<Scalar>().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<MappingStart>();
while (!parser.TryConsume<MappingEnd>(out _))
{
string key = parser.Consume<Scalar>().Value;
string value = parser.Consume<Scalar>().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<System.Runtime.CompilerServices.CompilerGeneratedAttribute>() != 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<System.Reflection.EventInfo> eventInfos = objectType.GetEvents(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)
.OrderBy(ei => ei.Name)
.AsEnumerable();
IEnumerable<FieldInfo> 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<PropertyInfo> 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<System.Reflection.EventInfo> Events, IEnumerable<FieldInfo> Fields, IEnumerable<PropertyInfo> Properties)
{
public static implicit operator (IEnumerable<System.Reflection.EventInfo> events, IEnumerable<FieldInfo> fields, IEnumerable<PropertyInfo> properties)(TypeData value) => (value.Events, value.Fields, value.Properties);
public static implicit operator TypeData((IEnumerable<System.Reflection.EventInfo> events, IEnumerable<FieldInfo> fields, IEnumerable<PropertyInfo> properties) value) => new TypeData(value.events, value.fields, value.properties);
}
}