Serialize and deserialize

edit

Strings

YamlSerializer.Serialize returns the YAML text of a value, and YamlSerializer.Deserialize<T> creates a value from YAML text:

var options = new YamlSerializerOptions
{
    PropertyNamingPolicy = YamlNamingPolicy.KebabCaseLower,
};

var yaml = YamlSerializer.Serialize(new Product
{
    Id = 1,
    DisplayName = "Sample product",
    Tags = ["new", "featured"],
}, options);

Assert.Equal("""
    id: 1
    name: Sample product
    tags:
      - new
      - featured

    """, yaml);

var product = YamlSerializer.Deserialize<Product>(yaml, options);
Assert.Equal("Sample product", product!.DisplayName);

The examples of this section use the Product class of Getting started.

Files and streams

Pass a Stream to read or write UTF-8 YAML, for instance a file:

private static void SaveProduct(string path, Product product)
{
    using var stream = File.Create(path);
    YamlSerializer.Serialize(stream, product); // Written as UTF-8
}

private static Product? LoadProduct(string path)
{
    using var stream = File.OpenRead(path);
    return YamlSerializer.Deserialize<Product>(stream);
}
Read fromWrite to
stringstring (the return value)
TextReader, read as the parser needs itTextWriter
Stream holding UTF-8 textStream, written as UTF-8
IBufferWriter<char>

The methods are synchronous. Each one has a generic form (Deserialize<T>) and a form that takes a Type, for a type known at run time only.

A stream can hold several documents separated by ---. Deserialize binds the first one; to read all of them, load the stream with YamlStream and convert each document with ToObject<T>.

Options

YamlSerializerOptions configures both directions. YamlSerializerOptions.Default holds the default values, and is used when you pass no options. The options are a record with init properties: create an instance with the values you need, or derive one from another with with:

var strict = YamlSerializerOptions.Default with
{
    UnmappedMemberHandling = YamlUnmappedMemberHandling.Disallow,
};

Assert.Equal(1, YamlSerializer.Deserialize<Product>("Id: 1\nPrice: 10")!.Id); // Price is ignored
Assert.Throws<YamlException>(() => YamlSerializer.Deserialize<Product>("Id: 1\nPrice: 10", strict));

An instance cannot change after it is created, so you can create it once, for instance in a static readonly field, and use it for every call.

Every method accepts, instead of the options, a YamlSerializerContext or a YamlTypeInfo produced by the source generator.

The options are described on the page of the feature they control:

Read without exceptions

TryDeserialize returns false instead of throwing a YamlException when the YAML is invalid or does not match the type:

if (YamlSerializer.TryDeserialize<Product>("Id: 1", out var value))
{
    Assert.Equal(1, value!.Id);
}

Assert.False(YamlSerializer.TryDeserialize<Product>("Id: not a number", out _));

Use it when invalid input is expected, for instance while probing the content of a file. To tell the user what is wrong, catch the exception instead: see Handling errors.