0

I want to implement Craig Andera's custom XML configuration handler in a slightly different scenario. What I want to be able to do is to read in a list of arbitrary length of custom objects defined as:

public class TextFileInfo
{
    public string Name { get; set; }
    public string TextFilePath { get; set; }
    public string XmlFilePath { get; set; }
}

I managed to replicate Craig's solution for one custom object but what if I want several?

Craig's deserialization code is:

public class XmlSerializerSectionHandler : IConfigurationSectionHandler
{
    public object Create(object parent, object configContext, XmlNode section)
    {
        XPathNavigator nav = section.CreateNavigator();
        string typename = (string)nav.Evaluate("string(@type)");
        Type t = Type.GetType(typename);
        XmlSerializer ser = new XmlSerializer(t);
        return ser.Deserialize(new XmlNodeReader(section));
    }
}

I think I could do this if I could get

Type t = Type.GetType("System.Collections.Generic.List<TextFileInfo>")

to work but it throws

Could not load type 'System.Collections.Generic.List<Test1.TextFileInfo>' from assembly 'Test1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.

1 Answer 1

5

I don't think this would work in that scenario. Craig's solution works well for simple object graphs, but collections are a little trickier. Lists are serialised as arrays, so putting your example in, a serialised List is something like:

<ArrayOfTextFileInfo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlsns:xsd="http://www.w3.org/2001/XMLSchema>
  <TextFileInfo>
    <Name>My Text File</Name>
    <TextFilePath>C:\MyTextFile.txt</TextFilePath>
    <XmlFilePath>C:\MyXmlFile.xml</XmlFilePath>
  </TextFileInfo>
</ArrayOfTextFileInfo>

Now, I'm guessing you could probably put that in a config, as long as the config section is named as "ArrayOfTextFileInfo". Not exactly that friendly. I think what you should probably do is use the standard configuration classes to build this:

public class TextFileConfigurationElement : ConfigurationElement
{
  [ConfigurationProperty("name", IsRequired = true, IsKey = true)]
  public string Name { 
    get { return (string)this["name"]; }
    set { this["name"] = value; }
  }

  [ConfigurationProperty("textFilePath")]
  public string TextFilePath {
    get { return (string)this["textFilePath"]; }
    set { this["textFilePath"] = value; }
  }

  [ConfigurationProperty("xmlFilePath")]
  public string XmlFilePath {
    get { return (string)this["xmlFilePath"]; }
    set { this["xmlFilePath"] = value; }
  }
}

[ConfigurationCollection(typeof(TextFileConfigurationElement))]
public class TextFileConfigurationElementCollection : ConfigurationElementCollection
{
  protected override void CreateNewElement() {
    return new TextFileConfigurationElement();
  }

  protected override object GetElementKey(ConfigurationElement element) {
    return ((TextFileConfigurationElement)element).Name;
  }
}

public class TextFilesConfigurationSection : ConfigurationSection
{
  [ConfigurationProperty("files")]
  public TextFileConfigurationElementCollection Files {
    get { return (TextFileConfigurationElementCollection)this["files"]; }
    set { this["files"] = value; }
  }

  public static TextFilesConfigurationSection GetInstance() {
    return ConfigurationManager.GetSection("textFiles") as TextFilesConfigurationSection;
  }
}

Once you've registered the config section:

<configSections>
  <add name="textFiles" type="...{type here}..." />
</configSections>

You can add in the configs:

<textFiles>
  <files>
    <add name="File01" textFilePath="C:\File01.txt" xmlTextFile="C:\File01.xml" />
  </files>
</textFiles>

Using that in code:

public List<TextFileInfo> GetFiles() {
  var list = new List<TextFileInfo>();

  var config = TextFileConfigurationSection.GetInstance();
  if (config == null)
    return list;

  foreach (TextFileConfigurationElement fileConfig in config.Files) {
    list.Add(new TextFileInfo 
                        {
                          Name = fileConfig.Name,
                          TextFilePath = fileConfig.TextFilePath,
                          XmlFilePath = fileConfig.XmlFilePath
                         });

  }

  return list;
}

Also, this:

Type t = Type.GetType("System.Collections.Generic.List<TextFileInfo>")

Won't work for a couple of reasons, you haven't fully qualified the TextFileInfo type (needs a namespace), and your definition of a generic type is wrong (I can see why you haven't specified it that way), it should look like:

Type t = Type.GetType("System.Collections.Generic.List`1[MyNamespace.TextFileInfo]");

Hope that helps!

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.