YamlDotNet
YamlDotNet copied to clipboard
Support "generic" (dotnet runtime types only) AOT
Is your feature request related to a problem? Please describe. I'm targetting AOT compilation and using YamlDotNet. The restriction to use only dotnet runtime types is fine in this particular context and since the (de)serialized structure(s) is not known upfront, using only List
Main issue is that out of the box the yamlcontext generators don't work for this use case and there is no default implementation.
Describe the solution you'd like Ideally it should work by building a default (De)Serializer - instead of testing Dictionary<,> it can test with object too (same for lists) and it should be close to work.
Describe alternatives you've considered Implement a custom static context (mainly a draft since it doesn't cover all cases):
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.ObjectFactories;
using YamlDotNet.Serialization.TypeInspectors;
namespace Workaround.YamlDotNet;
// note: aot generator doesn't work with generic containers so let's do it outselves
// StaticDeserializerBuilder has fallbacks (Dictionary<object, object>) and List<object>)
// in its constructor so let's just use them and only support that
public class YamlContext : StaticContext
{
public static readonly YamlContext Instance = new();
public override bool IsKnownType(Type type)
{
return false;
}
public override ITypeResolver GetTypeResolver() => SimpleTypeResolver.Instance;
public override StaticObjectFactory GetFactory() => SimpleStaticObjectFactory.Instance;
public override ITypeInspector GetTypeInspector() => SimpleTypeInspector.Instance;
}
internal class SimpleTypeInspector : TypeInspectorSkeleton
{
internal static readonly SimpleTypeInspector Instance = new();
public override IEnumerable<IPropertyDescriptor> GetProperties(Type type, object? container) =>
[];
public override string GetEnumName(Type enumType, string name) => name;
public override string GetEnumValue(object enumValue) => enumValue.ToString()!;
}
internal class SimpleStaticObjectFactory : StaticObjectFactory
{
internal static readonly SimpleStaticObjectFactory Instance = new();
public override object Create(Type type)
{
if (
typeof(Dictionary<object, object>) == type
|| typeof(IDictionary<object, object>) == type
)
{
return new OrderedDictionary<object, object>();
}
if (
typeof(Dictionary<string, object>) == type
|| typeof(IDictionary<string, object>) == type
)
{
return new OrderedDictionary<string, object>();
}
if (typeof(List<object>) == type || typeof(IList<object>) == type)
{
return new List<object>();
}
if (typeof(IList<IDictionary<string, object>>) == type)
{
return new List<IDictionary<string, object>>();
}
throw new InvalidOperationException($"Unknown type: '{type.FullName}'");
}
public override Array CreateArray(Type type, int count)
{
throw new NotImplementedException("shoudn't be called");
}
public override bool IsDictionary(Type type)
{
return type == typeof(Dictionary<object, object>)
|| type == typeof(IDictionary<object, object>)
|| type == typeof(IDictionary<string, object>)
|| type == typeof(IDictionary<string, string>)
|| type == typeof(Dictionary<string, string>)
|| type == typeof(IDictionary<string, int>)
|| type == typeof(Dictionary<string, int>)
|| type == typeof(IDictionary<string, bool>)
|| type == typeof(Dictionary<string, bool>)
|| type == typeof(IDictionary<string, double>)
|| type == typeof(Dictionary<string, double>)
|| type == typeof(IDictionary<object, string>)
|| type == typeof(Dictionary<object, string>)
|| type == typeof(IDictionary<object, int>)
|| type == typeof(Dictionary<object, int>)
|| type == typeof(IDictionary<object, bool>)
|| type == typeof(Dictionary<object, bool>)
|| type == typeof(IDictionary<object, double>)
|| type == typeof(Dictionary<object, double>);
}
public override bool IsArray(Type type)
{
return false;
}
public override bool IsList(Type type)
{
return type == typeof(List<object>)
|| type == typeof(IList<object>)
|| type == typeof(List<string>)
|| type == typeof(IList<string>)
|| type == typeof(List<bool>)
|| type == typeof(IList<bool>)
|| type == typeof(List<int>)
|| type == typeof(IList<int>);
}
public override Type GetKeyType(Type type)
{
if (
type == typeof(IDictionary<object, object>)
|| type == typeof(IDictionary<object, int>)
|| type == typeof(IDictionary<object, double>)
|| type == typeof(IDictionary<object, bool>)
|| type == typeof(Dictionary<object, object>)
|| type == typeof(Dictionary<object, int>)
|| type == typeof(Dictionary<object, double>)
|| type == typeof(Dictionary<object, bool>)
)
{
return typeof(object);
}
if (
type == typeof(IDictionary<string, object>)
|| type == typeof(IDictionary<string, int>)
|| type == typeof(IDictionary<string, double>)
|| type == typeof(IDictionary<string, bool>)
|| type == typeof(Dictionary<string, object>)
|| type == typeof(Dictionary<string, int>)
|| type == typeof(Dictionary<string, double>)
|| type == typeof(Dictionary<string, bool>)
)
{
return typeof(string);
}
return typeof(string); // normally it is always that so it is a safe default for us
}
public override Type GetValueType(Type type)
{
if (
type == typeof(Dictionary<object, object>)
|| type == typeof(List<object>)
|| type == typeof(IDictionary<object, object>)
|| type == typeof(IList<object>)
)
{
return typeof(object);
}
if (
type == typeof(List<IDictionary<string, object>>)
|| type == typeof(IList<IDictionary<string, object>>)
)
{
return typeof(IDictionary<string, object>);
}
if (
type == typeof(IDictionary<string, string>)
|| type == typeof(IDictionary<string, string>)
|| type == typeof(List<string>)
|| type == typeof(IList<string>)
)
{
return typeof(string);
}
if (
type == typeof(IDictionary<string, int>)
|| type == typeof(IDictionary<string, int>)
|| type == typeof(List<int>)
|| type == typeof(IList<int>)
)
{
return typeof(int);
}
if (
type == typeof(IDictionary<string, double>)
|| type == typeof(IDictionary<string, double>)
|| type == typeof(List<double>)
|| type == typeof(IList<double>)
)
{
return typeof(double);
}
if (
type == typeof(IDictionary<string, bool>)
|| type == typeof(IDictionary<string, bool>)
|| type == typeof(List<bool>)
|| type == typeof(IList<bool>)
)
{
return typeof(bool);
}
throw new InvalidOperationException($"Unknown type: '{type.FullName}'");
}
public override void ExecuteOnDeserializing(object value) { }
public override void ExecuteOnDeserialized(object value) { }
public override void ExecuteOnSerializing(object value) { }
public override void ExecuteOnSerialized(object value) { }
}
internal class SimpleTypeResolver : ITypeResolver
{
public static readonly SimpleTypeResolver Instance = new();
public Type Resolve(Type staticType, object? value)
{
return staticType;
}
}