---
title: "Getting started with oneyaml for C#"
url: https://recursiveai.org/oneyaml/csharp/docs/getting-started/
description: "Install the package, read YAML into C# types, write it back, edit a document in place and keep its comments, and apply JSON Patch and Merge Patch."
version: "1.2.1"
---

# Getting started with oneyaml for C#

Install OneYaml for .NET, read YAML into C# types, write it back, edit a file in place without losing its comments, and apply JSON Patch and Merge Patch.

OneYaml is lossless, comment-preserving editing and a System.Text.Json-style serializer for YAML 1.2, in one package. Both run on one parser, so a typed read and an edit never disagree about what a file means. Every sample on this page was compiled and run against OneYaml 1.2.1 from NuGet on .NET 10; the output under each one is what it printed.

## Install

OneYaml supports .NET 8 and .NET 10.

```sh
dotnet new console -o YamlStart
cd YamlStart
dotnet add package OneYaml --version 1.2.1
```

The namespace is `OneYaml`. The package has no dependencies outside .NET and no unsafe code, and a source generator in the package covers trimmed and native AOT apps. The license is MIT or Apache-2.0, at your choice.

The samples are complete `Program.cs` files with top-level statements. A few read a file from the current directory; the YAML they read is shown with them.

## Read YAML into a type

`YamlSerializer.Deserialize<T>` binds YAML to classes and records the way `JsonSerializer` binds JSON. Keys bind in camel case by default, so `replicas` fills `Replicas`.

```csharp
using OneYaml;

var cfg = YamlSerializer.Deserialize<Config>("""
    name: api
    replicas: 2
    ports: [80, 443]
    debug: false
    """)!;
string ports = string.Join(", ", cfg.Ports);
Console.WriteLine($"{cfg.Name} runs {cfg.Replicas} replicas on {ports} (debug: {cfg.Debug})");

record Config(string Name, int Replicas, List<int> Ports, bool Debug);
```

```text
api runs 2 replicas on 80, 443 (debug: False)
```

YAML 1.2 is the default: `yes` is a string and `0755` is the integer 755. A value that doesn’t fit its type is a `YamlException` with a line, a column and a path (see [errors with line and column](https://recursiveai.org/oneyaml/csharp/cookbook/errors-with-line-and-column/)).

## Write YAML

`YamlSerializer.Serialize` writes any value as block-style YAML.

```csharp
using OneYaml;

var cfg = new Config("api", 3, [80, 443], new() { ["tier"] = "web" });
Console.Write(YamlSerializer.Serialize(cfg));

record Config(
    string Name,
    int Replicas,
    List<int> Ports,
    Dictionary<string, string> Labels);
```

```text
name: api
replicas: 3
ports:
  - 80
  - 443
labels:
  tier: web
```

`YamlSerializerOptions` sets naming policies, ignore conditions, converters and more, as `JsonSerializerOptions` does ([Serializer](https://recursiveai.org/oneyaml/csharp/docs/serializer/)).

## Edit a file and keep its comments

To change a file that people also edit by hand, parse it into a `YamlDocument` instead. An edit changes only what you touch, in the old value’s style; comments and layout elsewhere stay byte for byte.

```csharp
using OneYaml;

using var doc = YamlDocument.Parse("""
    # Deploy settings
    name: api
    replicas: 2   # raised by the release job
    ports: [80]

    """);

// Read a value by JSON Pointer.
Console.WriteLine($"replicas = {doc.Get<int>("/replicas")}");

// Change a value, append to a list, add a key.
doc.Set("/replicas", 3);
doc.Add("/ports/-", 443);
doc.Root["region"] = "us-east-1";

Console.Write(doc);
```

```text
replicas = 2
# Deploy settings
name: api
replicas: 3   # raised by the release job
ports: [80, 443]
region: us-east-1
```

Paths are JSON Pointers (RFC 6901): `/replicas`, `/spec/containers/0/image`, and `/ports/-` for the end of a list. `doc.Root` reads like a `JsonNode`: `node["key"]`, `node[0]`, and assignment to change a value. A new value copies the style of its neighbors, so the flow list `[80]` stays a flow list ([Editing](https://recursiveai.org/oneyaml/csharp/docs/editing/)).

## Apply a JSON Patch or a Merge Patch

A `YamlDocument` applies RFC 6902 JSON Patch and RFC 7396 Merge Patch to the YAML text, atomically: if any operation fails, the document is unchanged.

```csharp
using OneYaml;

const string Src = """
    # Web service
    image: web:1.0   # pinned
    env:
      DEBUG: "1"
      LOG: info
    ports: [80]

    """;

// JSON Patch (RFC 6902): all or nothing.
using (var doc = YamlDocument.Parse(Src))
{
    doc.ApplyPatch("""
        [
          { "op": "replace", "path": "/image", "value": "web:1.1" },
          { "op": "add", "path": "/ports/-", "value": 443 }
        ]
        """);
    Console.Write(doc);
}
Console.WriteLine("---");

// Merge Patch (RFC 7396): null removes a key.
using (var doc = YamlDocument.Parse(Src))
{
    doc.ApplyMergePatch("""{ "env": { "DEBUG": null, "LOG": "warn" } }""");
    Console.Write(doc);
}
```

```text
# Web service
image: web:1.1   # pinned
env:
  DEBUG: "1"
  LOG: info
ports: [80, 443]
---
# Web service
image: web:1.0   # pinned
env:
  LOG: warn
ports: [80]
```

Patches use the YAML data model: `test` compares YAML values, so `1` and `1.0` differ. `ApplyPatch` also takes a `JsonNode` or a `YamlPatch` built in code ([Patches](https://recursiveai.org/oneyaml/csharp/docs/patches/)).

## Where next

Recipes, each a complete program:

- [Parse YAML into C# classes](https://recursiveai.org/oneyaml/csharp/cookbook/parse-yaml-into-classes/) and [read YAML without a class](https://recursiveai.org/oneyaml/csharp/cookbook/read-yaml-without-a-class/)
- [Edit YAML and keep its comments](https://recursiveai.org/oneyaml/csharp/cookbook/edit-yaml-keep-comments/) and [merge or patch YAML](https://recursiveai.org/oneyaml/csharp/cookbook/patch-yaml/)
- [Convert YAML to JSON and back](https://recursiveai.org/oneyaml/csharp/cookbook/yaml-to-json/) and [read Kubernetes manifests and multi-document files](https://recursiveai.org/oneyaml/csharp/cookbook/kubernetes-multi-document/)
- [Report errors with line and column](https://recursiveai.org/oneyaml/csharp/cookbook/errors-with-line-and-column/), [anchors, aliases and merge keys](https://recursiveai.org/oneyaml/csharp/cookbook/anchors-aliases-merge-keys/)
- [Read untrusted YAML safely](https://recursiveai.org/oneyaml/csharp/cookbook/untrusted-input/), [use YAML in a native AOT app](https://recursiveai.org/oneyaml/csharp/cookbook/native-aot/) and [switch from YamlDotNet with one package](https://recursiveai.org/oneyaml/csharp/cookbook/switch-from-yamldotnet/)

The docs:

- [Serializer](https://recursiveai.org/oneyaml/csharp/docs/serializer/), [editing](https://recursiveai.org/oneyaml/csharp/docs/editing/), [patches](https://recursiveai.org/oneyaml/csharp/docs/patches/), [limits](https://recursiveai.org/oneyaml/csharp/docs/limits/) and the [reader and emitter](https://recursiveai.org/oneyaml/csharp/docs/reader-emitter/). The API reference is the XML documentation your IDE shows.
- Moving from another library: [YamlDotNet](https://recursiveai.org/oneyaml/csharp/docs/migrate-yamldotnet/) and [SharpYaml](https://recursiveai.org/oneyaml/csharp/docs/migrate-sharpyaml/), with the compatibility packages [OneYaml.Compat.YamlDotNet](https://recursiveai.org/oneyaml/csharp/docs/compat-yamldotnet/) and [OneYaml.Compat.SharpYaml](https://recursiveai.org/oneyaml/csharp/docs/compat-sharpyaml/).
- Choosing a library: [how it compares](https://recursiveai.org/oneyaml/csharp/docs/comparison/), [performance](https://recursiveai.org/oneyaml/csharp/docs/performance/) and [conformance](https://recursiveai.org/oneyaml/csharp/docs/conformance/).
- What changed and what stays stable: the [changelog](https://recursiveai.org/oneyaml/csharp/docs/changelog/) and [stability](https://recursiveai.org/oneyaml/csharp/docs/stability/).

Choosing a C# YAML library: [YamlDotNet, SharpYaml, VYaml and OneYaml compared](https://recursiveai.org/oneyaml/articles/csharp-yaml-parser/).
