---
title: "Replacing YamlDotNet"
url: https://recursiveai.org/oneyaml/articles/yamldotnet-alternative/
description: "Looking for a YamlDotNet alternative? What YamlDotNet does to a file you edit and save, the options, and a one-package swap that keeps your builder code."
date: 2026-10-26
author: oneyaml team
---

# Replacing YamlDotNet

Looking for a YamlDotNet alternative? What YamlDotNet does to a file you edit and save, the options, and a one-package swap that keeps your builder code.

October 26, 2026 · oneyaml team

Developers look for a YamlDotNet alternative for three reasons: loading a YAML file into its `YamlStream`, changing a value and saving it drops every comment and blank line, its typed deserializer reads YAML 1.1 (`yes` is `true`, `0755` is 493), and native AOT needs a separate generator package. The main alternatives are SharpYaml 3 (System.Text.Json-style, YAML 1.2), VYaml (fast, Unity-first) and OneYaml, which edits files without touching their comments and has a compatibility package, `OneYaml.Compat.YamlDotNet`, that keeps your `DeserializerBuilder` code compiling after a package swap.

## What YamlDotNet does to a file you edit and save

Here is a typical hand-maintained file:

*values.yaml*

```
# values.yaml for the web service
image:
  repository: registry.example.com/web
  tag: "1.4.2"   # set by CI
 
replicas: 2      # scaled by the release job
 
# Hosts served by the ingress
hosts:
  - example.com
  - www.example.com
```

The usual YamlDotNet way to change one value is to load it into a `YamlStream`, as in its [wiki sample](https://github.com/aaubry/YamlDotNet/wiki/Samples.LoadingAYamlStream), change the node, and save:

*Program.cs · YamlDotNet 18.1.0 · .NET 10*

```
using YamlDotNet.RepresentationModel;
 
var stream = new YamlStream();
using (var reader = new StreamReader("values.yaml"))
{
    stream.Load(reader);
}
 
var root = (YamlMappingNode)stream.Documents[0].RootNode;
root.Children[new YamlScalarNode("replicas")] = new YamlScalarNode("3");
 
stream.Save(Console.Out, assignAnchors: false);
```

*Output*

```
image:
  repository: registry.example.com/web
  tag: "1.4.2"
replicas: 3
hosts:
- example.com
- www.example.com
...
```

With YamlDotNet 18.1.0, all four comments and both blank lines are gone, the list is no longer indented under `hosts`, and a `...` document-end marker has been added. The quotes around `"1.4.2"` survive. Write that back to a file in a repository and the diff covers the whole file.

The same edit with OneYaml’s `YamlDocument`, and two more:

*Program.cs · OneYaml 1.2.1 · .NET 10*

```
using OneYaml;
 
using var doc = YamlDocument.Parse(File.ReadAllText("values.yaml"));
 
doc.Set("/image/tag", "1.5.0");         // stays double-quoted
doc.Set("/replicas", 3);                // its comment stays
doc.Add("/hosts/-", "api.example.com"); // block style, like its siblings
 
File.WriteAllText("values.yaml", doc.ToString());
Console.Write(doc);
```

*Output*

```
# values.yaml for the web service
image:
  repository: registry.example.com/web
  tag: "1.5.0"   # set by CI
 
replicas: 3      # scaled by the release job
 
# Hosts served by the ingress
hosts:
  - example.com
  - www.example.com
  - api.example.com
```

Only the three values change; every comment, blank line and quote stays. Each value keeps its style, and the new host is laid out like its siblings. More on what an edit keeps in [edit YAML without losing comments](https://recursiveai.org/oneyaml/articles/edit-yaml-keep-comments/) and the [C# recipe](https://recursiveai.org/oneyaml/csharp/cookbook/edit-yaml-keep-comments/).

## Swap YamlDotNet for OneYaml with one package

Rewriting every call site isn’t needed to start. `OneYaml.Compat.YamlDotNet` provides YamlDotNet’s serializer builders and representation model under new namespaces, tested against YamlDotNet 18.1.0. Take a YamlDotNet program that reads a file with a YAML 1.1 `yes` in it:

*Program.cs · YamlDotNet 18.1.0 · .NET 10*

```
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;
 
IDeserializer deserializer = new DeserializerBuilder()
    .WithNamingConvention(CamelCaseNamingConvention.Instance)
    .IgnoreUnmatchedProperties()
    .Build();
var service = deserializer.Deserialize<Service>("name: web\nreplicas: 2\npublic: yes\nlegacy: true\n");
Console.WriteLine($"{service.Name} x{service.Replicas}, public: {service.Public}");
 
ISerializer serializer = new SerializerBuilder()
    .WithNamingConvention(CamelCaseNamingConvention.Instance)
    .Build();
Console.Write(serializer.Serialize(service));
 
class Service
{
    public string Name { get; set; } = "";
    public int Replicas { get; set; }
    public bool Public { get; set; }
}
```

*Output*

```
web x2, public: True
name: web
replicas: 2
public: true
```

Swap the package and the two `using` lines:

*Terminal · NuGet*

```
dotnet remove package YamlDotNet
dotnet add package OneYaml.Compat.YamlDotNet --version 1.2.1
```

*Program.cs · OneYaml.Compat.YamlDotNet 1.2.1 · .NET 10*

```
using OneYaml.Compat.YamlDotNet.Serialization;                   // was: YamlDotNet.Serialization
using OneYaml.Compat.YamlDotNet.Serialization.NamingConventions; // was: YamlDotNet.Serialization.NamingConventions
 
IDeserializer deserializer = new DeserializerBuilder()
    .WithNamingConvention(CamelCaseNamingConvention.Instance)
    .IgnoreUnmatchedProperties()
    .Build();
var service = deserializer.Deserialize<Service>("name: web\nreplicas: 2\npublic: yes\nlegacy: true\n");
Console.WriteLine($"{service.Name} x{service.Replicas}, public: {service.Public}");
 
ISerializer serializer = new SerializerBuilder()
    .WithNamingConvention(CamelCaseNamingConvention.Instance)
    .Build();
Console.Write(serializer.Serialize(service));
 
class Service
{
    public string Name { get; set; } = "";
    public int Replicas { get; set; }
    public bool Public { get; set; }
}
```

*Output*

```
web x2, public: True
name: web
replicas: 2
public: true
```

The output is identical: the adapter reads YAML 1.1 forms such as `yes` the way YamlDotNet does, so existing files keep their meaning. The package page lists what it covers and every difference ([OneYaml.Compat.YamlDotNet](https://recursiveai.org/oneyaml/csharp/docs/compat-yamldotnet/)).

### What the adapter doesn’t cover

- **Doesn’t compile:** the scanner, `MergingParser`, type inspectors, node deserializers, event emitters, object-graph visitors, static contexts and comment events.
- **Throws `NotSupportedException`:** `Deserialize(IParser)` with a parser that isn’t the adapter’s own, and a serializer built with `EnsureRoundtrip()`.
- **Behaves differently:** serializer output can quote more, more valid YAML is read, exception classes can differ, and depth and alias limits always apply.
- **Still drops comments:** after you change a node, the adapter’s `YamlStream.Save` writes without comments, as YamlDotNet’s does. Use `YamlDocument` for edits.

The order to check them in is in [migrating from YamlDotNet](https://recursiveai.org/oneyaml/csharp/docs/migrate-yamldotnet/), with the `OneYaml` API for each YamlDotNet call. Move file by file: the adapter and the `OneYaml` package work side by side.

## YamlDotNet alternatives compared

|  | SharpYaml 3.16.0 | VYaml 1.5.0 | OneYaml 1.2.1 |
|---|---|---|---|
| API | System.Text.Json style ([README](https://github.com/xoofx/SharpYaml)) | `[YamlObject]` partial types ([README](https://github.com/hadashiA/VYaml)) | System.Text.Json style, plus `YamlDocument` |
| YAML version | 1.2 Core by default, other schemas selectable | “YAML 1.2 mostly supported” | 1.2 Core |
| `yes` into a `bool`, `0755` into an `int` (measured) | refused, 755 | not measured | refused, 755 |
| Native AOT | Source generator in the package | Source generator | Source generator in the package |
| Targets (NuGet) | .NET Standard 2.0, .NET 8, .NET 10 | .NET Standard 2.0 and 2.1, .NET 6, 8, 9; Unity | .NET 8, .NET 10 |
| Migration from YamlDotNet | New API | New API | Compatibility package, then new API |
| Edit a file and keep comments | Not documented as such; README lists a syntax tree API | Not documented | `YamlDocument` |
| JSON Patch and Merge Patch on YAML | Not documented | Not documented | RFC 6902 and RFC 7396 ([how](https://recursiveai.org/oneyaml/articles/json-patch-yaml/)) |
| License | MIT | MIT | MIT OR Apache-2.0 |

The measured row comes from the program in [choosing a C# YAML library](https://recursiveai.org/oneyaml/articles/csharp-yaml-parser/), run on 26 October 2026.

## When to stay on YamlDotNet

- You target .NET Framework or .NET Standard: OneYaml runs on .NET 8 and .NET 10 only.
- You depend on type inspectors, node deserializers or event emitters, which neither the adapter nor OneYaml provides.
- You read and write YAML 1.1 files as YAML 1.1 and nothing edits them by hand.
- Your code works and nothing above is a problem for you. YamlDotNet is maintained: 18.1.0 shipped in June 2026.

For the full picture of .NET YAML libraries, see [choosing a C# YAML library](https://recursiveai.org/oneyaml/articles/csharp-yaml-parser/); to start with OneYaml directly, the [getting started guide](https://recursiveai.org/oneyaml/csharp/docs/getting-started/) and the [swap recipe](https://recursiveai.org/oneyaml/csharp/cookbook/switch-from-yamldotnet/).
