---
title: "Parse YAML into C# classes"
url: https://recursiveai.org/oneyaml/csharp/cookbook/parse-yaml-into-classes/
description: "Read a YAML file into C# classes, records and enums with OneYaml’s YamlSerializer, with naming policies, defaults, required keys and an error for unknown keys."
version: "1.2.1"
---

# Parse YAML into C# classes

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:

```yaml
# 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:

```csharp
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 }
```

```text
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 `PropertyNamingPolicy` and `EnumNamingPolicy` are kebab case, so `interval-seconds` fills `IntervalSeconds` and `on-failure` reads as `Restart.OnFailure`.
- **Defaults** are the property initializers: `replicas` isn’t in the file, so `Replicas` stays 1. `[YamlRequired]` makes a missing key an error.
- **Unknown keys:** `UnmappedMemberHandling = YamlUnmappedMemberHandling.Disallow` makes a key the type doesn’t have an error with its position and path. `SourceName` puts the file name in front of every message.
- `PORT: "8080"` is quoted, so it is a string. YAML 1.2 is the default: plain `yes` is a string, and `0755` is the integer 755.
- `[YamlPropertyName]`, `[YamlConverter]` and polymorphism work as their System.Text.Json counterparts do ([Serializer](https://recursiveai.org/oneyaml/csharp/docs/serializer/)).

When the shape isn’t known ahead of time, read nodes from a `YamlDocument`: [read YAML without a class](https://recursiveai.org/oneyaml/csharp/cookbook/read-yaml-without-a-class/).
