Skip to content

Instantly share code, notes, and snippets.

@borland
Created January 14, 2021 06:01
Show Gist options
  • Save borland/3a8a0a8a56b3a4ef315e1f83f5ab4073 to your computer and use it in GitHub Desktop.
Save borland/3a8a0a8a56b3a4ef315e1f83f5ab4073 to your computer and use it in GitHub Desktop.
Experimental code for configuring an ASP.NET Core app using KDL rather than JSON
// NOTE: This requires you to have a reference to KDL for .NET, and import the kdl-net namespace
// https://github.com/borland/kdl-net
//
// Highly experimental, but it works.
//
// NOTE: Once you have this code in your project, you can use
//
// var config = new ConfigurationBuilder()
// .AddKdlFile("appsettings.kdl", optional: false, reloadOnChange: false)
//
// in the same way you would for JSON
public static class KdlConfigurationExtensions
{
public static IConfigurationBuilder AddKdlFile(this IConfigurationBuilder builder, string path, bool optional, bool reloadOnChange)
=> AddKdlFile(builder, null, path, optional, reloadOnChange);
public static IConfigurationBuilder AddKdlFile(this IConfigurationBuilder builder, IFileProvider provider, string path, bool optional, bool reloadOnChange)
{
return builder.AddKdlFile(s => {
s.FileProvider = provider;
s.Path = path;
s.Optional = optional;
s.ReloadOnChange = reloadOnChange;
s.ResolveFileProvider();
});
}
public static IConfigurationBuilder AddKdlFile(this IConfigurationBuilder builder, Action<KdlConfigurationSource> configureSource)
=> builder.Add(configureSource);
class KdlConfigurationProvider : FileConfigurationProvider
{
public KdlConfigurationProvider(KdlConfigurationSource source) : base(source) { }
public override void Load(Stream stream)
{
try
{
Data = KdlConfigurationFileParser.Parse(stream);
}
catch (KDLParseException e)
{
throw new FormatException("Invalid KDL file", e);
}
}
}
public class KdlConfigurationSource : FileConfigurationSource
{
public override IConfigurationProvider Build(IConfigurationBuilder builder)
{
EnsureDefaults(builder);
return new KdlConfigurationProvider(this);
}
}
static class KdlConfigurationFileParser
{
public static Dictionary<string, string> Parse(Stream stream)
{
var doc = new KDLParser().Parse(stream);
var context = new Stack<string>();
var result = new Dictionary<string, string>();
VisitKdlDoc(doc, context, result);
return result;
}
public static void VisitKdlDoc(KDLDocument doc, Stack<string> context, Dictionary<string, string> result)
{
foreach (var node in doc.Nodes)
{
context.Push(node.Identifier);
if (node.Child != null)
{
VisitKdlDoc(node.Child, context, result);
}
else if (node.Args.Count > 0) // we only support really basic trivial KDL of [key value] or key { subkey value }, nothing else fancier or multiple args, props etc
{
result[string.Join(':', context)] = node.Args[0].AsString().Value;
}
context.Pop();
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment