Suppose you have a Shape
class and derived classes Rectangle
, Circle
, and Triangle
:
public abstract class Shape;
public sealed class Rectangle : Shape
{
[YamlMember(Alias = "width")]
public int? Width { get; set; }
[YamlMember(Alias = "height")]
public int? Height { get; set; }
}
public sealed class Circle : Shape
{
[YamlMember(Alias = "radius")]
public int? Radius { get; set; }
}
public sealed class Triangle : Shape
{
[YamlMember(Alias = "side1")]
public int? Side1 { get; set; }
[YamlMember(Alias = "side2")]
public int? Side2 { get; set; }
[YamlMember(Alias = "side3")]
public int? Side3 { get; set; }
}
You may want to deserialize an arbitrary list of YAML-serialized shapes into the corresponding C# objects. To achieve this, YamlDotNet allows you to define a special YAML key as a type discriminator. The value associated with this key determines the type of object to instantiate.
Let’s create a YAML array of shapes, adding a type
discriminator:
- type: rectangle
width: 10
height: 20
- type: circle
radius: 5
- type: triangle
side1: 6
side2: 9
side3: 5
We can build a deserializer and use the WithTypeDiscriminatingNodeDeserializer
method to specify the type discriminator and the mapping of types to each discriminator value:
var deserializer = new DeserializerBuilder()
.IgnoreUnmatchedProperties() // Prevent YamlDotNet from throwing if it doesn't find a C# property for the discriminator key.
.WithTypeDiscriminatingNodeDeserializer(options =>
{
options.AddKeyValueTypeDiscriminator<Shape>("type", new Dictionary<string, Type>(StringComparer.Ordinal)
{
["rectangle"] = typeof(Rectangle),
["circle"] = typeof(Circle),
["triangle"] = typeof(Triangle),
});
})
.Build();
This deserializer can handle the YAML payload polymorphically as a collection of Shape
objects:
var yaml = /*lang=yaml*/
"""
- type: rectangle
# rest of the YAML from above
""";
var shapes = deserializer.Deserialize<Shape[]>(yaml);
Inspecting the shapes
variable using Rider’s collection visualizer confirms the correct types have been deserialized. To help with debugging, I used the [DebuggerDisplay]
attribute to display a user-friendly string representation of the shapes:
You can learn more about YamlDotNet’s polymorphic deserialization in the official documentation. I skipped an example of another kind of discriminator based on the existence of a specific property in the C# class, as it’s a less common use case.