C
C#16mo ago
fooo1

✅ Reading a yaml file that has multiple types

I'm having trouble finding examples that show how, given a key, to return a value which may be a string, int or list.
---
foo1: some string
foo2:
- a
- b
- 123
foo3: 123
---
foo1: some string
foo2:
- a
- b
- 123
foo3: 123
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration.Yaml;

public class YamlLib {
public dynamic ReadYaml(string key) {
var basePath = AppContext.BaseDirectory.Substring(0, AppContext.BaseDirectory.IndexOf("bin"));
var configFileName = Path.Combine(basePath, "appsettings.yml");

var builder = new ConfigurationBuilder();
builder.AddYamlFile(configFileName);
var configuration = builder.Build();

// var value = configuration[key];
// var value = configuration.GetSection(key).GetChildren().Select(x => x.Value).ToList();
return value;
}
}
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration.Yaml;

public class YamlLib {
public dynamic ReadYaml(string key) {
var basePath = AppContext.BaseDirectory.Substring(0, AppContext.BaseDirectory.IndexOf("bin"));
var configFileName = Path.Combine(basePath, "appsettings.yml");

var builder = new ConfigurationBuilder();
builder.AddYamlFile(configFileName);
var configuration = builder.Build();

// var value = configuration[key];
// var value = configuration.GetSection(key).GetChildren().Select(x => x.Value).ToList();
return value;
}
}
var value = configuration[key]; works on primitive types only; var value = configuration.GetSection(key).GetChildren().Select(x => x.Value).ToList(); works(ish) on list but not on primitive types. Whats the path of least complexity to get any value from any key, leaving up to the user to make sure correct methods are used on correct types?
8 Replies
Angius
Angius16mo ago
https://github.com/aaubry/YamlDotNet Deserialize to
class Stuff
{
public string Foo1 { get; set; }
public List<string> Foo2 { get; set; }
public int Foo3 { get; set; }
}
class Stuff
{
public string Foo1 { get; set; }
public List<string> Foo2 { get; set; }
public int Foo3 { get; set; }
}
If something can be either a string, a list, or an int, then it's garbage data you're working with and may God have mercy on your soul
ero
ero16mo ago
no that's pretty usual for yaml actually
Angius
Angius16mo ago
OOF
Foxtrek_64
Foxtrek_6416mo ago
GitHub
GitHub - mcintyre321/OneOf: Easy to use F#-like ~discriminated~ uni...
Easy to use F#-like ~discriminated~ unions for C# with exhaustive compile time matching - GitHub - mcintyre321/OneOf: Easy to use F#-like ~discriminated~ unions for C# with exhaustive compile time ...
Foxtrek_64
Foxtrek_6416mo ago
So if you can't change the data you're getting, this could be a way to get around it
class Stuff
{
public string Foo1 { get; set; }
public OneOf<string, int> Foo2 { get; set; }
public int Foo3 { get; set; }
}
class Stuff
{
public string Foo1 { get; set; }
public OneOf<string, int> Foo2 { get; set; }
public int Foo3 { get; set; }
}
You may need to add a converter for your yaml library so it knows how to handle it
fooo1
fooo116mo ago
that still requires defining types for all keys in yaml. I suppose I'm wondering (coming from python, so very new) is there a way to query for a value without first defining the types? i.e. dynamic value = configuration[key]; whereas value could be anything - list, string - and leaving up to the user to interact with value correctly?
Angius
Angius16mo ago
I mean, you could use object worse comes to worst But how do you reason about it? If it happens to be a list, you can't do thing + 7, but you can do that if it's an int If it's a string you can't .Add() to it, but you can if it's a list Etc
fooo1
fooo116mo ago
Here's the solution I came up with - intermediate deserialization to json, this way I avoid having to define a model. Would you say there are any immediate shortcomings, other than everything being string by default?
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;
using System.Text.Json.Nodes;

public static class GetConfigFromYaml {
public static JsonNode GetKey(string key) {
var configName = $"conf.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "dev"}.yml";

// deserialize yml-string to yml
var obj = new StringReader(File.ReadAllText(configName));
var yamlObj = new Deserializer().Deserialize(obj);

// serialize yml to json-string
var serializer = new SerializerBuilder()
.WithNamingConvention(UnderscoredNamingConvention.Instance)
.JsonCompatible()
.Build();
var json = serializer.Serialize(yamlObj);

// deserialize json-string to json
var jsonObj = JsonNode.Parse(json)!;

return jsonObj[key] ?? throw new Exception($"key {key} not found in {configName}");

}
}
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;
using System.Text.Json.Nodes;

public static class GetConfigFromYaml {
public static JsonNode GetKey(string key) {
var configName = $"conf.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "dev"}.yml";

// deserialize yml-string to yml
var obj = new StringReader(File.ReadAllText(configName));
var yamlObj = new Deserializer().Deserialize(obj);

// serialize yml to json-string
var serializer = new SerializerBuilder()
.WithNamingConvention(UnderscoredNamingConvention.Instance)
.JsonCompatible()
.Build();
var json = serializer.Serialize(yamlObj);

// deserialize json-string to json
var jsonObj = JsonNode.Parse(json)!;

return jsonObj[key] ?? throw new Exception($"key {key} not found in {configName}");

}
}
invoking
var foo = GetConfigFromYaml.GetKey("foo").ToString();
Console.WriteLine($"foo value: {foo}");

var fooList = GetConfigFromYaml.GetKey("foo_list").AsArray().Select(x => x!.ToString()).ToList();
Console.WriteLine(fooList[0]);

var fooDict = GetConfigFromYaml.GetKey("foo_dict").AsObject().ToDictionary(x => x.Key, x => x.Value!.ToString());
Console.WriteLine(fooDict["key1"]);
var foo = GetConfigFromYaml.GetKey("foo").ToString();
Console.WriteLine($"foo value: {foo}");

var fooList = GetConfigFromYaml.GetKey("foo_list").AsArray().Select(x => x!.ToString()).ToList();
Console.WriteLine(fooList[0]);

var fooDict = GetConfigFromYaml.GetKey("foo_dict").AsObject().ToDictionary(x => x.Key, x => x.Value!.ToString());
Console.WriteLine(fooDict["key1"]);