<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.9.5">Jekyll</generator><link href="https://brows.app/feed.xml" rel="self" type="application/atom+xml" /><link href="https://brows.app/" rel="alternate" type="text/html" /><updated>2024-02-17T04:46:02+00:00</updated><id>https://brows.app/feed.xml</id><title type="html">Brows</title><subtitle>Brows is a Windows file manager for power users.</subtitle><entry><title type="html">Configure .NET apps without JSON or XML</title><link href="https://brows.app/post/configure-net-apps-without-json-or-xml/" rel="alternate" type="text/html" title="Configure .NET apps without JSON or XML" /><published>2024-02-16T00:00:00+00:00</published><updated>2024-02-16T00:00:00+00:00</updated><id>https://brows.app/post/configure-net-apps-without-json-or-xml</id><content type="html" xml:base="https://brows.app/post/configure-net-apps-without-json-or-xml/"><![CDATA[<p>XML and JSON have long been the configuration formats of choice in the .NET ecosystem.
For something entirely different, continue reading.</p>

<p><strong><a href="https://github.com/kyourek/domore">Domore.Conf</a></strong> configures POCO classes with a syntax that resembles a programming
language but is less strict in its use.</p>

<blockquote>
  <p>This is the first is a series of posts that describes how <a href="https://github.com/brows-app/brows">Brows</a> leverages
simple text for configuration and data.</p>
</blockquote>

<p>Consider the following class.</p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">MyConfig</span> <span class="p">{</span>
    <span class="k">public</span> <span class="kt">string</span> <span class="n">Theme</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
    <span class="k">public</span> <span class="n">DayOfWeek</span> <span class="n">WeekendStartsOn</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>A configuration file for that class might look like this.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>My config . Theme = light
my config . Weekend starts on = Saturday
</code></pre></div></div>
<p>If that configuration file exists alongside an application’s <em>.exe</em> with the same name as
the <em>.exe</em> but with different extension <em>.conf</em>, an instance may be configured by the following.</p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">var</span> <span class="n">obj</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">MyConfig</span><span class="p">();</span>
<span class="n">Conf</span><span class="p">.</span><span class="nf">Configure</span><span class="p">(</span><span class="n">obj</span><span class="p">);</span>
</code></pre></div></div>
<p>I’m sure you can guess what the output will be.</p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="n">obj</span><span class="p">.</span><span class="n">Theme</span><span class="p">);</span>           <span class="c1">// light</span>
<span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="n">obj</span><span class="p">.</span><span class="n">WeekendStartsOn</span><span class="p">);</span> <span class="c1">// Saturday</span>
</code></pre></div></div>

<blockquote>
  <p>If the file-naming convention described above was confusing, here’s an example that should clear
things up. An executable named <strong>my-process.exe</strong> would have a configuration file in the same
directory with the name <strong>my-process.conf</strong>.</p>
</blockquote>

<hr />
<h3 id="so-far-things-seem-pretty-simple-lets-make-them-simpler">So far things seem pretty simple. Let’s make them simpler.</h3>
<p>Change the configuration file to look like this.</p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Theme</span> <span class="p">=</span> <span class="n">dark</span>
<span class="n">Weekend</span> <span class="n">starts</span> <span class="k">on</span> <span class="p">=</span> <span class="n">Saturday</span>
</code></pre></div></div>
<p>And configure the object with a blank key.</p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Conf</span><span class="p">.</span><span class="nf">Configure</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="n">key</span><span class="p">:</span> <span class="s">""</span><span class="p">);</span>
</code></pre></div></div>
<p>If a key is not provided, or is <code class="language-plaintext highlighter-rouge">null</code>, the name of the type will be used to prefix configuration 
properties. If a blank key is passed (<code class="language-plaintext highlighter-rouge">string.Empty</code>), each line is a potential configuration
property.</p>

<p>That behavior allows some interesting conveniences. Suppose the class above is subclassed.</p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">PartyAnimal</span> <span class="p">:</span> <span class="n">MyConfig</span> <span class="p">{</span> <span class="p">}</span>
<span class="k">class</span> <span class="nc">CollegeStudent</span> <span class="p">:</span> <span class="n">PartyAnimal</span> <span class="p">{</span> <span class="p">}</span>
</code></pre></div></div>
<p>And the configuration changes to this.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Theme = dark
Weekend starts on = Friday
college student . weekend starts on = thursday
</code></pre></div></div>
<p>Instances of each class may be configured by either or both a blank and <code class="language-plaintext highlighter-rouge">null</code> key.</p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">var</span> <span class="n">pa</span> <span class="p">=</span> <span class="n">Conf</span><span class="p">.</span><span class="nf">Configure</span><span class="p">(</span><span class="k">new</span> <span class="nf">PartyAnimal</span><span class="p">(),</span> <span class="n">key</span><span class="p">:</span> <span class="s">""</span><span class="p">);</span>
<span class="kt">var</span> <span class="n">cs</span> <span class="p">=</span> <span class="n">Conf</span><span class="p">.</span><span class="nf">Configure</span><span class="p">(</span><span class="k">new</span> <span class="nf">CollegeStudent</span><span class="p">(),</span> <span class="n">key</span><span class="p">:</span> <span class="s">""</span><span class="p">);</span>
<span class="n">Conf</span><span class="p">.</span><span class="nf">Configure</span><span class="p">(</span><span class="n">cs</span><span class="p">);</span>
</code></pre></div></div>
<p>The college student was configured twice, first with a blank key to gather generic configuration for
all instances of <code class="language-plaintext highlighter-rouge">MyConfig</code>, then with a key matching the name of the type. The output is as follows.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Console.WriteLine(pa.Theme);           // dark
Console.WriteLine(pa.WeekendStartsOn); // Friday
Console.WriteLine(cs.Theme);           // dark
Console.WriteLine(cs.WeekendStartsOn); // Thursday
</code></pre></div></div>
<p>Both the party animal and the college student got the same configuration for their theme, but the
college student has more specific configuration for the starting day of his weekend.</p>

<blockquote>
  <p>Notice that a new instance of each type was passed to <code class="language-plaintext highlighter-rouge">Conf.Configure</code>.
That same instance was returned from the method.</p>
</blockquote>

<hr />
<h3 id="lets-set-some-rules-that-weve-seen-so-far">Let’s set some rules that we’ve seen so far.</h3>
<ul>
  <li>Whitespace in configuration keys is ignored.</li>
  <li>Configuration keys are case-insensitive.</li>
  <li>Configuration key parts are separated by a dot (<code class="language-plaintext highlighter-rouge">.</code>).</li>
</ul>

<hr />

<p>Now let’s clean up that college student and send him to class.</p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">CollegeStudent</span> <span class="p">{</span>
    <span class="k">public</span> <span class="n">List</span><span class="p">&lt;</span><span class="n">DayOfWeek</span><span class="p">&gt;</span> <span class="n">ClassSchedule</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>The class schedule could be configured with this <em>.conf</em> file.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>student.class schedule[0] = monday
student.class schedule[1] = wednesday
student.class schedule[2] = friday
</code></pre></div></div>
<p>But that’s a little clunky. It’s more readable and easier to maintain like this.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>student.class schedule = monday, Wednesday, Friday
</code></pre></div></div>

<blockquote>
  <p>In the above example, neither a blank key nor the type name was used as the key.
Rather, <code class="language-plaintext highlighter-rouge">student</code> may be used by calling <code class="language-plaintext highlighter-rouge">Conf.Configure</code> with an arbitrary
key, i.e.:</p>

  <p><code class="language-plaintext highlighter-rouge">Conf.Configure(new CollegeStudent(), key: "student")</code>.</p>
</blockquote>

<hr />
<h3 id="college-isnt-that-easy-its-a-little-more-complex">College isn’t <em>that</em> easy. It’s a little more complex.</h3>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">CollegeProfessor</span> <span class="p">{</span>
    <span class="k">public</span> <span class="kt">string</span> <span class="n">Name</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
<span class="p">}</span>
<span class="k">class</span> <span class="nc">CollegeClass</span> <span class="p">{</span> 
    <span class="k">public</span> <span class="n">CollegeProfessor</span> <span class="n">Professor</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
    <span class="k">public</span> <span class="n">List</span><span class="p">&lt;</span><span class="n">DayOfWeek</span><span class="p">&gt;</span> <span class="n">Schedule</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
    <span class="k">public</span> <span class="n">TimeSpan</span> <span class="n">Time</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
<span class="p">}</span>
<span class="k">class</span> <span class="nc">CollegeStudent</span> <span class="p">{</span>
    <span class="k">public</span> <span class="n">Dictionary</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">,</span> <span class="n">CollegeClass</span><span class="p">&gt;</span> <span class="n">Class</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>That student’s schedule can be configured with this <em>.conf</em> file by calling
<code class="language-plaintext highlighter-rouge">Conf.Configure(new CollegeStudent(), key: "")</code>.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>class[Chemistry 101].time           = 8:00
class[Chemistry 101].schedule       = Monday, Wednesday, Friday
class[Chemistry 101].professor.name = Marie Curie
class[ Physics 200 ].time           = 10:30
class[ Physics 200 ].schedule       = Tuesday, Wednesday
class[ Physics 200 ].professor.name = Georges Lemaître
</code></pre></div></div>
<blockquote>
  <p>Dictionary keys follow a few rules different from standard keys.</p>
  <ul>
    <li>Whitespace before and after the key is ignored.</li>
    <li>Whitespace <em>within</em> the key is respected.</li>
    <li>Case is respected. To ignore the case of a dictionary’s keys, create it with a
case-insensitive comparer.
      <ul>
        <li><code class="language-plaintext highlighter-rouge">new Dictionary&lt;string, string&gt;(StringComparer.OrdinalIgnoreCase)</code>.</li>
      </ul>
    </li>
  </ul>
</blockquote>

<hr />
<h3 id="thats-it-for-now-but-theres-more-to-cover">That’s it for now, but there’s more to cover.</h3>
<p>In a future post, I’ll go over custom configuration parsing, attributes for configuring
your configuration, and reversing the whole process to convert a POCO class into a
configuration file.</p>

<p><a href="https://www.codeproject.com/script/Articles/BlogFeedList.aspx?amid=16204319" rel="tag" style="display:none">CodeProject</a></p>]]></content><author><name>the Brows app developers</name></author><category term="Domore" /><category term="Domore.Conf" /><category term="Conf" /><category term="Config" /><category term="Configuration" /><summary type="html"><![CDATA[XML and JSON have long been the configuration formats of choice in the .NET ecosystem. For something entirely different, continue reading.]]></summary></entry></feed>