Skip to content

YAML Conversion

recipe JSON Schema C# yaml conversion

This recipe demonstrates how to convert between YAML and JSON using the Corvus.Text.Json.Yaml library. It covers YAML→JSON parsing, JSON→YAML conversion, schema modes, multi-document handling, anchors and aliases, block scalars, streaming conversion, and the Utf8YamlWriter.

The Pattern

YAML is a human-friendly data serialization format commonly used for configuration files, CI pipelines, Kubernetes manifests, and API specifications. The Corvus.Text.Json.Yaml converter transforms YAML content into JSON with zero-allocation on the hot path, and converts JSON back to canonical YAML via the Utf8YamlWriter, supporting all YAML 1.2 features and four schema modes.

Parsing YAML to a Document

The simplest approach parses YAML to a strongly-typed ParsedJsonDocument<JsonElement>:

string yaml = """
    name: Alice
    age: 30
    hobbies:
      - reading
      - cycling
    """;

using ParsedJsonDocument<JsonElement> doc = YamlDocument.Parse<JsonElement>(yaml);
JsonElement root = doc.RootElement;
Console.WriteLine($"Name: {root.GetProperty("name").GetString()}");
Console.WriteLine($"Age:  {root.GetProperty("age").GetInt32()}");

Output:

Name: Alice
Age:  30

Converting to a JSON String

When you need the raw JSON text:

string json = YamlDocument.ConvertToJsonString("key: value");
// {"key":"value"}

Schema Modes

The YAML Core Schema (default) resolves plain scalars to typed JSON values:

string: hello
integer: 42
hex: 0xFF
octal: 0o77
float: 3.14
infinity: .inf
not_a_number: .nan
null_value: null
tilde_null: ~
boolean: true

The JSON Schema mode is stricter — only JSON-compatible patterns are recognized as typed values. The Failsafe Schema treats everything as strings. YAML 1.1 compatibility mode adds yes/no/on/off booleans.

Anchors and Aliases

YAML anchors define reusable content; aliases reference it:

defaults: &defaults
  adapter: postgres
  host: localhost

development:
  database: dev_db
  <<: *defaults

With YamlSchema.Yaml11, the merge key (<<) expands the anchor inline.

Block Scalars

Literal blocks (|) preserve newlines; folded blocks (>) join lines:

literal: |
  Line 1
  Line 2

folded: >
  This is a long
  paragraph.

Chomping indicators (- strip, + keep) control trailing newlines.

Multi-Document Streams

YAML files can contain multiple documents separated by ---:

---
name: first
---
name: second

Use YamlDocumentMode.MultiAsArray to parse all documents into a JSON array.

JSON to YAML Conversion

Convert JSON content to canonical YAML:

string json = """{"name":"Alice","scores":[95,87,92]}""";
string yaml = YamlDocument.ConvertToYamlString(json);
// name: Alice
// scores:
//   - 95
//   - 87
//   - 92

From a pre-parsed document

When you already have a parsed document, the element walk avoids re-parsing:

using ParsedJsonDocument<JsonElement> doc = ParsedJsonDocument<JsonElement>.Parse(jsonBytes);
JsonElement root = doc.RootElement;
string yaml = YamlDocument.ConvertToYamlString(in root);

Streaming to an IBufferWriter

For zero-allocation output (aside from the pooled buffer):

ArrayBufferWriter<byte> buffer = new(1024);
YamlDocument.ConvertToYaml(doc.RootElement, buffer);
ReadOnlySpan<byte> yamlBytes = buffer.WrittenSpan;

Using Utf8YamlWriter directly

For fine-grained control over the YAML output:

ArrayBufferWriter<byte> output = new(256);
Utf8YamlWriter writer = new(output);

try
{
    writer.WriteStartMapping();
    writer.WritePropertyName("enabled"u8);
    writer.WriteBooleanValue(true);
    writer.WritePropertyName("count"u8);
    writer.WriteNumberValue("42"u8);
    writer.WriteEndMapping();
    writer.Flush();
}
finally
{
    writer.Dispose();
}

Event Parsing

For advanced scenarios, EnumerateEvents provides zero-allocation, callback-based event enumeration:

YamlDocument.EnumerateEvents(
    "items:\n  - one\n  - two",
    (in YamlEvent e) =>
    {
        Console.WriteLine($"[{e.Line}:{e.Column}] {e.Type}");
        return true; // return false to stop early
    });

The YamlEvent ref struct provides the event type, scalar value (UTF-8 bytes), anchor, tag, scalar style, and line/column position. Its spans point directly into the source buffer and are only valid during the callback.

Running the Example

cd docs/ExampleRecipes/027-Yaml
dotnet run