chore: some experimentations with DotNetYaml
This commit is contained in:
parent
cddb30c631
commit
c205e710bc
18
Engine.Serialization/Engine.Serialization.csproj
Normal file
18
Engine.Serialization/Engine.Serialization.csproj
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<RootNamespace>Syntriax.Engine.Serialization</RootNamespace>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Engine.Core\Engine.Core.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="YamlDotNet" Version="16.3.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
172
Engine.Serialization/EntityFieldConverter.cs
Normal file
172
Engine.Serialization/EntityFieldConverter.cs
Normal file
@ -0,0 +1,172 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
6
Engine.Serialization/EntityReference.cs
Normal file
6
Engine.Serialization/EntityReference.cs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
namespace Syntriax.Engine.Serialization;
|
||||||
|
|
||||||
|
public class EntityReference
|
||||||
|
{
|
||||||
|
public string Id { get; set; }
|
||||||
|
}
|
5
Engine.Serialization/IgnoreSerializationAttribute.cs
Normal file
5
Engine.Serialization/IgnoreSerializationAttribute.cs
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
namespace Syntriax.Engine.Serialization;
|
||||||
|
|
||||||
|
public class IgnoreSerializationAttribute : Attribute
|
||||||
|
{
|
||||||
|
}
|
30
Engine.Serialization/Serializer.cs
Normal file
30
Engine.Serialization/Serializer.cs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
using YamlDotNet.Serialization;
|
||||||
|
using YamlDotNet.Serialization.NamingConventions;
|
||||||
|
|
||||||
|
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 IDeserializer deserializer = new DeserializerBuilder()
|
||||||
|
.WithNamingConvention(PascalCaseNamingConvention.Instance)
|
||||||
|
.WithTypeConverter(new EntityConverter())
|
||||||
|
.WithTypeConverter(new Vector2DConverter())
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
public static string Serialize(object instance)
|
||||||
|
{
|
||||||
|
return serializer.Serialize(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static T Deserialize<T>(string yaml)
|
||||||
|
{
|
||||||
|
return deserializer.Deserialize<T>(yaml);
|
||||||
|
}
|
||||||
|
}
|
@ -11,6 +11,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Engine.Systems", "Engine.Sy
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Engine", "Engine\Engine.csproj", "{58AE79C1-9203-44AE-8022-AA180F0A71DC}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Engine", "Engine\Engine.csproj", "{58AE79C1-9203-44AE-8022-AA180F0A71DC}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Engine.Serialization", "Engine.Serialization\Engine.Serialization.csproj", "{9B46BE51-DD6B-4EDD-AAA8-41E993D7422C}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@ -36,5 +38,9 @@ Global
|
|||||||
{58AE79C1-9203-44AE-8022-AA180F0A71DC}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{58AE79C1-9203-44AE-8022-AA180F0A71DC}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{58AE79C1-9203-44AE-8022-AA180F0A71DC}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{58AE79C1-9203-44AE-8022-AA180F0A71DC}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{58AE79C1-9203-44AE-8022-AA180F0A71DC}.Release|Any CPU.Build.0 = Release|Any CPU
|
{58AE79C1-9203-44AE-8022-AA180F0A71DC}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{9B46BE51-DD6B-4EDD-AAA8-41E993D7422C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{9B46BE51-DD6B-4EDD-AAA8-41E993D7422C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{9B46BE51-DD6B-4EDD-AAA8-41E993D7422C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{9B46BE51-DD6B-4EDD-AAA8-41E993D7422C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Engine.Core\Engine.Core.csproj" />
|
<ProjectReference Include="..\Engine.Core\Engine.Core.csproj" />
|
||||||
|
<ProjectReference Include="..\Engine.Serialization\Engine.Serialization.csproj" />
|
||||||
<ProjectReference Include="..\Engine.Systems\Engine.Systems.csproj" />
|
<ProjectReference Include="..\Engine.Systems\Engine.Systems.csproj" />
|
||||||
<ProjectReference Include="..\Engine.Physics2D\Engine.Physics2D.csproj" />
|
<ProjectReference Include="..\Engine.Physics2D\Engine.Physics2D.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user