Let me just start off by saying that YAML itself is not that popular in either C# or .NET. For a long time, under .NET Framework, XML seemed to rein supreme with things like csproj files, solution files and even msbuild configurations all being XML driven. That slowly changed to be more JSON friendly, and I think we could all agree that things like NewtonSoft.Json/JSON.NET had a huge impact on pretty much every .NET developer using JSON these days.
That being said, there are times when you need to parse YAML. Recently, on a project that involved other languages such as Go, Python and PHP, YAML was chosen as a shared configuration type between all languages. Don’t get me started on why this was the case…. But it happened. And so if you are stuck working out how to parse YAML files in C#, then this guide is for you.
Introducing YamlDotNet
In .NET, there is no support for reading or writing YAML files out of the box. Unlike things like JSON Serializer and XML Serializers, you aren’t able to rely on Microsoft for this one. Luckily, there is a nuget package that is more or less the absolute standard when it comes to working with YAML in C#. YamlDotNet.
To install it, from our package manager console we just have to run :
Install-Package YamlDotNet
And we are ready to go!
Deserializing YAML To A POCO
Deserializing YAML directly to a POCO is actually simple!
Let’s say we have a YAML file that looks like so :
databaseConnectionString: Server=.;Database=myDataBase; uploadFolder: /uploads/ approvedFileTypes : [.png, .jpeg, .jpg]
And we then have a plain C# class that is set up like the following :
class Configuration { public string DatabaseConnectionString { get; set; } public string UploadFolder { get; set; } public List<string> ApprovedFileTypes { get; set; } }
The code to deserialize this is just a few lines long :
var deserializer = new YamlDotNet.Serialization.DeserializerBuilder() .WithNamingConvention(CamelCaseNamingConvention.Instance) .Build(); var myConfig = deserializer.Deserialize<Configuration>(File.ReadAllText("config.yaml"));
Easy right! But I do want to point out one big caveat to all of this. YamlDotNet by default is *not* case insensitive. Infact, it’s actually somewhat frustrating that you must match the casing perfectly. Maybe that’s just me being spoiled with JSON.NET’s excellent case insensitivity, but it is annoying here.
You must use one of the following :
- CamelCase Naming
- Hyphenated Naming
- LowerCase Naming
- PascalCase Naming
- Underscored Naming
- Or simply have the YAML match the casing of your properties exactly
But you can’t mix up casing that easily unfortunately.
YamlDotNet does have a “YamlMember” attribute that works much the same as JsonProperty in JSON.NET. However, you must also override the ApplyNamingConventions property to be false for it to really work properly. e.g. If in my YAML I have “Database_ConnectionString”, I need to apply an alias *as well* as remove the camel case naming convention otherwise it will look for “database_ConnectionString”.
[YamlMember(Alias = "Database_ConnectionString", ApplyNamingConventions = false)] public string DatabaseConnectionString { get; set; }
Deserializing YAML To A Dynamic Object
If you check my guide on parsing JSON, you’ll notice I talk about things like JObject, JsonPath, dynamic JTokens etc. Basically, ways to read a JSON File, without having the structured class to deserialize into.
In my brief time working with YamlDotNet, it doesn’t seem to have the same functionality. It looks to be either you serialize into a class, or nothing at all. There are some work arounds however, you can for example deserialize into a dynamic object.
dynamic myConfig = deserializer.Deserialize<ExpandoObject>(File.ReadAllText("config.yaml"));
But, it isn’t quite the same as the ability to use things like JsonPath to find deep seated nodes. What I will say is that that probably has more to do with where and how JSON is used vs YAML. It’s generally going to be pretty rare to have a YAML file be hundreds or thousands of lines long (Although not unheard of), so the need for things like JsonPath is maybe in the edge case territory.
Serializing A C# Object To YAML
Writing a C# object into YAML is actually pretty straight forward. If we take our simple C# configuration class we had before :
class Configuration { public string DatabaseConnectionString { get; set; } public string UploadFolder { get; set; } public List<string> ApprovedFileTypes { get; set; } }
We can do everything in just 4 lines :
var config = new Configuration(); var serializer = new SerializerBuilder() .WithNamingConvention(CamelCaseNamingConvention.Instance) .Build(); var stringResult = serializer.Serialize(config);
I’ll note a couple of things about the serializing/writing process :
- If an object is null, it will still be serialized (with an empty value), but you can override this if you want
- Naming Convention is uber important here obviously, with the default being whatever casing your properties are in with your C# code
But outside of those points, it’s really straight forward and just works a treat.