Recursive AI

Use YAML in a native AOT app

All oneyaml C# recipes

The YamlSerializer overloads that take options use reflection, which trimming and native AOT can’t rely on. Declare a YamlSerializerContext and pass its generated metadata instead. The generator ships in the OneYaml package; there is nothing else to install.

XML
<PropertyGroup>
  <PublishAot>true</PublishAot>
</PropertyGroup>
C#
using OneYaml;
 
// Generated metadata instead of reflection: safe to trim and to publish as native AOT.
string src = """
    retries: 3
    endpoint: https://example.com
    tags: [web, public]
    """;
Settings settings = YamlSerializer.Deserialize(src, AppYamlContext.Default.Settings)!;
Console.WriteLine($"{settings.Retries} retries against {settings.Endpoint.Host}");
 
Console.Write(YamlSerializer.Serialize(settings with { Retries = 5 }, AppYamlContext.Default.Settings));
 
// YamlDocument takes generated metadata too.
using var doc = YamlDocument.Parse(src);
doc.Set("/tags", new List<string> { "web", "internal" }, AppYamlContext.Default.ListString);
Console.WriteLine(doc);
 
record Settings(int Retries, Uri Endpoint, List<string> Tags);
 
[YamlSerializable(typeof(Settings))]
partial class AppYamlContext : YamlSerializerContext;
Text
3 retries against example.com
retries: 5
endpoint: https://example.com
tags:
  - web
  - public
retries: 3
endpoint: https://example.com
tags: [web, internal]

That output is from the native executable built by dotnet publish -c Release, with warnings treated as errors.

Good to know

  • The generator writes a YamlTypeInfo<T> property on the context for each [YamlSerializable] type and for every type those reach, such as ListString for List<string> above.
  • Generated and reflection metadata bind the same way: attributes, converters, polymorphism and extension data work with both.
  • For options other than the defaults, create the context with them, new AppYamlContext(new YamlSerializerOptions { ... }), or set TypeInfoResolver = AppYamlContext.Default on options you already pass around.
  • With PublishTrimmed or PublishAot, reflection-based binding is off by default. Set the MSBuild property YamlSerializerIsReflectionEnabledByDefault to true to keep it.
  • Mistakes in a context, such as a context that isn’t partial, are build-time diagnostics OY0001 to OY0010.

More in source generation and native AOT.

Trim warnings, publish results and YamlDotNet compared: C# YAML with native AOT.