1 year ago
#369224
andb70
Deserialize YAML to default values when top level node is optional
I use YamlDotNet.Serialization in my application and I plan to introduce a new node in my config.yaml but I want my app to be compatible with current version of config.
I found here a suggestion to manage optional mappings but it doensn't fit my scenario where the entire info
node will be missing when the old yaml is used.
In this case if I try to access the config.Informations.Version
I get the exception Object reference not set to an instance of an object
, because the config.Informations
object hasn't been created.
Is there a way to obtain a valid config.Informations
with default values?
old yaml:
sections:
db:
...
names:
...
new yaml:
info:
lastEdit: 2022-04-04
...
sections:
db:
...
names:
...
Config.cs:
using System.Collections.Generic;
using System.ComponentModel;
using YamlDotNet.Serialization;
namespace myApp
{
public class Config
{
[YamlMember(Alias = "info", ApplyNamingConventions = false)]
[DefaultValue()] // <<<<<<<<<< what should I use here?
public DocumentInformations Informations { get; set; }
[YamlMember(Alias = "sections", ApplyNamingConventions = false)]
public Sections Sections { get; set; }
}
public class DocumentInformations
{
[YamlMember(Alias = "lastEdit", ApplyNamingConventions = false)]
[DefaultValue("?")]
public string LastEdit { get; set; }
[YamlMember(Alias = "lastEditor", ApplyNamingConventions = false)]
[DefaultValue("?")]
public string LastEditor { get; set; }
[YamlMember(Alias = "version", ApplyNamingConventions = false)]
[DefaultValue("?")]
public string Version { get; set; }
[YamlMember(Alias = "notes", ApplyNamingConventions = false)]
[DefaultValue("")]
public string Notes { get; set; }
}
public class Sections
{
[YamlMember(Alias = "db", ApplyNamingConventions = false)]
public Dictionary<string, Dictionary<string, string>> DB { get; set; }
[YamlMember(Alias = "names", ApplyNamingConventions = false)]
public Dictionary<string, Dictionary<string, string>> Names { get; set; }
}
}
program.cs:
string filepath = $"{path}\\{project}\\config.yaml";
using StreamReader reader = File.OpenText(filepath);
var deserializer = new DeserializerBuilder()
.IgnoreUnmatchedProperties()
.Build();
var config = deserializer.Deserialize<Config>(reader);
Console.WriteLine($"Using Config version {config.Informations.Version}");
Found a solution
Having found that the DefaultValue
attribute is not useful to initialize a property, I have slightly modified my code which now works, but I would like to know if there is a better way to do it, without putting code in the ctor:
public class Config
{
public Config() => Informations = new DocumentInformations();
[YamlMember(Alias = "info", ApplyNamingConventions = false)]
public DocumentInformations Informations { get; set; }
...
}
public class DocumentInformations
{
[YamlMember(Alias = "lastEdit", ApplyNamingConventions = false)]
public string LastEdit { get; set; } = "<N/D>";
[YamlMember(Alias = "lastEditor", ApplyNamingConventions = false)]
public string LastEditor { get; set; } = "<N/D>";
[YamlMember(Alias = "version", ApplyNamingConventions = false)]
public string Version { get; set; } = "<N/D>";
[YamlMember(Alias = "notes", ApplyNamingConventions = false)]
public string Notes { get; set; } = "";
}
c#
yaml
default
toplevel
0 Answers
Your Answer