The XmlConfigurationProvider loads configuration XML data from file and along with the ConfigurationBuilder. It really is an interesting that allows simple XML, INI or JSON files to be import data directly into you code with little or no effort.

config.SetBasePath(Directory.GetCurrentDirectory());
config.AddXmlFile( "config.xml", optional: true, reloadOnChange: true);

In my case I was using this feature as a mechanism to deserialize the data straight into objects that I could use via dependency injection. The dasBlog configs file contained, what I thought, was a relatively simple xml snippet and I was interested in getting the contents of validCommentTags in order to validate which types of tags could be associated with comments submitted to the blog:

<SiteConfig>
   <validCommentTags>
      <tag name="a" attributes="href,title" allowed="true" />
      <tag name="b" attributes="" allowed="true"/>
   </validCommentTags>
</SiteConfig>

I found that the Config providers, XML provider in this instance, loaded data into key-value pairs format as shown below:

{[validCommentTags:tag:a:name, a]}
{[validCommentTags:tag:a:attributes, href,title]}
{[validCommentTags:tag:a:allowed, true]}
{[validCommentTags:tag:b:name, b]}
{[validCommentTags:tag:b:attributes, ]}
{[validCommentTags:tag:b:allowed, true]}

The config binding system would convert this to objects, I would create a matching class as follows and then use dependency injection to pull this into any class as needed:

public interface ISiteConfig
{
    [XmlArray("validCommentTags", IsNullable = true)]
    [XmlArrayItem("tag", Type = typeof(ValidTag))]
    ValidTag[] ValidCommentTagArray { get; set; }
}

Unfortunately none of the “tags” would ever get deserialized. I think I tried just about every possible combination!

So how did I solve this? I really did not, a colleague let me in on some of the finer points of XML providers. First of all the config providers really don't know anything about the object you're going to bind to, in practice then, XML serialization attributes (like XMLArray and XMLArrayItem) have no impact.

In order to support arrays, the config provider has to use numeric keys when loading the values into the key-value pairs, for example validCommentTags:tag:0:name, validCommentTags:tag:1:name, etc. It looks like the XML provider does not do this by default, instead it just builds a hierarchical name by concatenating all the parent element names together.

I got this to work by explicitly declaring binding to a numeric key, as in “name=0”.

<SiteConfig>
   <validCommentTags>
      <tag name="0" tagname="a" attributes="href,title" allowed="true" />
      <tag name="1" tagname="a" attributes="" allowed="true"/>
   </validCommentTags>
</SiteConfig>

Then redesigned my classes/interfaces like this:

public class ValidCommentTags
{
    public List<Tag> Tag { get; set; }
}
public interface ISiteConfig
{
    public ValidCommentTags[] ValidCommentTags { get; set; }
}

This took a while to figure out, I hope it helps someone else.



Comment Section

Comments are closed.