1

I am working on a c# console application which has an xml config file which contains the settings for the program.

I wanted to add a comment to the xml file to show what values you can use for a particular settings using <!--My Comment-->. For some reason when I put this in, its as if C# then thinks that this is end of the file and no other part of the file is read, there's no errors being thrown and the program doesn't stop responding it continues running the rest of the code.

Below is the config file.

<?xml version="1.0" encoding="utf-8" ?>
<options>
  <database>
    <item key="server" value="localhost" />
    <item key="database" value="emailserver" />
    <item key="username" value="myusername" />
    <item key="password" value="mypassword" />
    <item key="port" value="3306" />
  </database>
  <EmailServer>
    <item key="logFile" value="email_server.txt" />
    <!--You can use fileCopy or database-->
    <item key="logManageMode" value="fileCopy" />
    <item key="ip_address" value="127.0.0.1" />
    <item key="smtpPort" value="26" />
    <item key="requireAuthentication" value="false" />
  </EmailServer>
</options>

If I don't put that comment in it reads in the entire file. Below is the code that reads the XML file.

public Dictionary<string, string> readConfig(string sectionName, bool soapService=false, Dictionary<string, string> config=null)
        {
            Dictionary<string, string> newConfig = null;
            if (config == null)
            {
                newConfig = new Dictionary<string, string>();
            }
            //Dictionary<string, string> config = new Dictionary<string, string>();
            try
            {
                XmlDocument configXml = new XmlDocument();
                string configPath = "";
                if (soapService)
                {
                    string applicationPath = HttpContext.Current.Server.MapPath(null);
                    configPath = Path.Combine(applicationPath, "config.xml");
                    configXml.Load(configPath);
                }
                else
                {
                    configXml.Load("config.xml");
                }

                XmlNodeList options = configXml.SelectNodes(string.Format("/options/{0}", sectionName));
                XmlNodeList parameters = configXml.GetElementsByTagName("item");
                foreach (XmlNode option in options)
                {
                    foreach (XmlNode setting in option)
                    {
                        string key = setting.Attributes["key"].Value;
                        string value = setting.Attributes["value"].Value;

                        if (config == null)
                        {
                            newConfig.Add(key, value);
                        }
                        else
                        {
                            config.Add(key, value);
                        }
                    }
                }
            }
            catch (KeyNotFoundException ex)
            {
                Console.WriteLine("Config KeyNotFoundException: {0}", ex.Message);
            }
            catch (XmlException ex)
            {
                Console.WriteLine("Config XmlException: {0}", ex.Message);
            }
            catch (Exception ex)
            {
                Console.WriteLine("Config Exception: {0}", ex.Message);
                Console.WriteLine("StackTrace: {0}", ex.StackTrace);
            }
            if (config == null)
            {
                return newConfig;
            }
            return config;
        }

Thanks for any help you can provide.

3
  • Slightly off-topic, but if you use app.config then don't have to parse the XML yourself. Commented Aug 15, 2012 at 17:45
  • Side note: please consider providing smaller sample and reasonable guess at error behavior - your current code have 2 distinct part (load XML and read nodes) and your problem statement "thinks that this is end of the file and no other part of the file is read" imply error in first half while there was absolutely nothing wrong with reading XML (which could have been easily verified by looking at the configXml object in debugger). Commented Aug 15, 2012 at 17:53
  • I disagree with Alexei. The added code was welcome context without being too much. Commented Aug 15, 2012 at 20:06

2 Answers 2

4

your comment is a node. so when you loop over the nodes:

 foreach (XmlNode option in options) 
                 { 
                     foreach (XmlNode setting in option) 
                     { 
                         string key = setting.Attributes["key"].Value; 
                         string value = setting.Attributes["value"].Value;

you are tripping your try/catch block at the commebnt because that node does not contain the "key" or "value" attributes.

You can use the NodeType property to determine if the node is a comment or not. For example:

 foreach (XmlNode option in options) 
                 { 
                     foreach (XmlNode setting in option) 
                     { 
                         if (setting.NodeType == XmlNodeType.Comment)
                         {
                             continue;
                         }
                         string key = setting.Attributes["key"].Value; 
                         string value = setting.Attributes["value"].Value;

Another option is to continue if the node is not an element:

if (setting.NodeType != XmlNodeType.Element)

As per Tomalak: Actually this is just as brittle as the OP's original code. Insert a different node type into the XML than a comment and it will blow up again. Just select a specific XmlNodeList inside the loop: XmlNodeList settings = option.SelectNodes("item[@key and @value]");

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

2 Comments

Actually this is just as brittle as the OP's original code. Insert a different node type into the XML than a comment and it will blow up again. Just select a specific XmlNodeList inside the loop: XmlNodeList settings = option.SelectNodes("item[@key and @value]");
Thanks worked great, I didn't notice it go into the catch section of the code it didn't print anything to the console that I noticed
1

Here's a version of your code that does not choke on unknown XML node types and also is a little shorter.

The key point is that you cannot simply loop over a list of child nodes and expect the nodes to be something specific.

Always select something specific - element nodes with the name item that contain an @key and a @value attribute in this case - if you want to work on specific nodes.

XmlNodeList settings = option.SelectNodes("./item[@key and @value]");

Here's the full code:

public Dictionary<string, string> readConfig(string sectionName, bool soapService=false, Dictionary<string, string> config=null)
{
    Dictionary<string, string> myConfig = config ?? new Dictionary<string, string>();
    try
    {
        XmlDocument configXml = new XmlDocument();
        string configPath = "config.xml";
        if (soapService)
        {
            string applicationPath = HttpContext.Current.Server.MapPath(null);
            configPath = Path.Combine(applicationPath, "config.xml");
        }
        configXml.Load(configPath);

        XmlNodeList options = configXml.SelectNodes(string.Format("/options/{0}", sectionName));
        foreach (XmlNode option in options)
        {
            XmlNodeList settings = option.SelectNodes("./item[@key and @value]");
            foreach (XmlNode setting in settings)
            {
                myConfig.Add(setting.Attributes["key"].Value, setting.Attributes["value"].Value);
            }
        }
    }
    catch (KeyNotFoundException ex)
    {
        Console.WriteLine("Config KeyNotFoundException: {0}", ex.Message);
    }
    catch (XmlException ex)
    {
        Console.WriteLine("Config XmlException: {0}", ex.Message);
    }
    catch (Exception ex)
    {
        Console.WriteLine("Config Exception: {0}", ex.Message);
        Console.WriteLine("StackTrace: {0}", ex.StackTrace);
    }
    return myConfig;
}

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.