Handling errors

edit

Catch YAML errors

Every error of the content, from invalid YAML to a value that does not match its type, throws a YamlException or a derived class. Start and End give the position of the node at fault:

static string? Validate(string yaml)
{
    try
    {
        YamlSerializer.Deserialize<Product>(yaml);
        return null;
    }
    catch (YamlException exception)
    {
        // Line and Column are zero-based
        return $"line {exception.Start.Line + 1}, column {exception.Start.Column + 1}";
    }
}

Assert.Null(Validate("Id: 1"));
Assert.Equal("line 2, column 7", Validate("Id: 1\nTags: 3"));
Assert.Equal("line 1, column 5", Validate("Id: [1"));

Other exceptions report a problem in your code rather than in the YAML, and are not meant to be caught: a NotSupportedException for a type that cannot be serialized, an InvalidOperationException for an invalid configuration of your types, such as two members with the same key.

Name the file in the messages

Set SourceName, typically to the path of the file, to start the messages with it:

var options = new YamlSerializerOptions { SourceName = "config.yaml" };

var exception = Assert.Throws<YamlException>(() => YamlSerializer.Deserialize<Product>("Id: [1, 2]", options));
Assert.Equal("config.yaml: (Lin: 0, Col: 4, Chr: 4) - (Lin: 0, Col: 4, Chr: 4): Expected a Scalar token but found 'StartSequence'.", exception.Message);

The message gives the start and end positions as (Lin, Col, Chr), from 0. YamlException.SourceName returns the name.

Read without exceptions

TryDeserialize returns false instead of throwing a YamlException:

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

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

It does not tell what is wrong: use it when invalid input is expected and its cause does not matter, and catch the exception when you report the error.