Read a YAML file into typed C# data: a class with defaults, a record, a dictionary, an optional section and an enum. YamlSerializer takes options shaped like JsonSerializerOptions, and honors the System.Text.Json attributes where no YAML attribute is present.
The file:
# service.yaml name: web image: nginx:1.27 env: LOG_LEVEL: info PORT: "8080" healthcheck: path: /healthz interval-seconds: 10 restart: on-failure
The program:
using System.Text.Json; using OneYaml; var options = new YamlSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.KebabCaseLower, EnumNamingPolicy = JsonNamingPolicy.KebabCaseLower, UnmappedMemberHandling = YamlUnmappedMemberHandling.Disallow, SourceName = "service.yaml", }; var svc = YamlSerializer.Deserialize<Service>(File.ReadAllText("service.yaml"), options)!; Console.WriteLine($"{svc.Name} runs {svc.Image} x{svc.Replicas}, restart {svc.Restart}"); Console.WriteLine($"env: {string.Join(", ", svc.Env.Select(e => $"{e.Key}={e.Value}"))}"); if (svc.Healthcheck is { } h) { Console.WriteLine($"health check: {h.Path} every {h.IntervalSeconds}s"); } // Disallow: a key the type doesn't have is an error, not ignored. try { YamlSerializer.Deserialize<Service>("name: web\nimgae: nginx\n", options); } catch (YamlException e) { Console.WriteLine(e.Message); } class Service { [YamlRequired] public string Name { get; set; } = ""; public string Image { get; set; } = ""; public int Replicas { get; set; } = 1; public Dictionary<string, string> Env { get; set; } = []; public Healthcheck? Healthcheck { get; set; } public Restart Restart { get; set; } } record Healthcheck(string Path, int IntervalSeconds); enum Restart { Always, OnFailure, Never }
web runs nginx:1.27 x1, restart OnFailure env: LOG_LEVEL=info, PORT=8080 health check: /healthz every 10s service.yaml: The key 'imgae' is not a member of Service. (line 2, column 1, path /imgae)
Good to know
- Naming: keys bind in camel case by default. Here
PropertyNamingPolicyandEnumNamingPolicyare kebab case, sointerval-secondsfillsIntervalSecondsandon-failurereads asRestart.OnFailure. - Defaults are the property initializers:
replicasisn’t in the file, soReplicasstays 1.[YamlRequired]makes a missing key an error. - Unknown keys:
UnmappedMemberHandling = YamlUnmappedMemberHandling.Disallowmakes a key the type doesn’t have an error with its position and path.SourceNameputs the file name in front of every message. PORT: "8080"is quoted, so it is a string. YAML 1.2 is the default: plainyesis a string, and0755is the integer 755.[YamlPropertyName],[YamlConverter]and polymorphism work as their System.Text.Json counterparts do (Serializer).
When the shape isn’t known ahead of time, read nodes from a YamlDocument: read YAML without a class.