Load XML into List Collection

I needed the ability to load a XML file into a selectable list. The problem was really two fold, I had a collection of DAT files (like a single column CSV file) that I needed to first convert to XML (I had another existing collection of XML files, yea I know "consistency, consistency, consistency") as well.ScreenShot014

So I can up with a simple couple of utility methods to convert a single column CSV file (herein referred to as a DAT file) to an XML and then another method to take a XML file and return a list collection. This allows me to first convert the DAT and then load the collection into a ComboBox. 

At first I was worried about performance of reading from the XML files, but then I realized we are talking about a collection of strings around the order of 100. This loads using the XMLTextReader class in about 2/10 of a second. I think that is performance I can live with.

This method takes a plurized DAT file (much like the RoR MVC style) and creates a simple XML (that includes the root attributes that the partner XML read method expects). An example would be using a DAT file name ‘colors.dat’ it would make a XML file with ‘colors’ root node and then a collection of ‘color’ nodes with a single element named ‘name’ that contains the value of each DAT file.

Example DAT file (just for clarity):

red
yellow
orange
pink
blue

Method to convert the DAT to a XML file:

        public void DATToXML(string DATFile, string XMLFile, bool SortList, bool IncludeEmpty, string IncludeAllCaption)
        {
            XmlWriterSettings settings = new XmlWriterSettings();
            settings.Indent = true;
            settings.IndentChars = ("    ");
            settings.Encoding = System.Text.Encoding.UTF8;
            
 
            FileInfo datFileInfo = new FileInfo(DATFile);
            string rootNode = datFileInfo.Name.Replace(datFileInfo.Extension.ToString(), "");
            string childNode = rootNode.Substring(0, rootNode.Length - 1);
 
            using (StreamReader sr = new StreamReader(DATFile))
            {
                string line = string.Empty;
 
                using (XmlWriter writer = XmlWriter.Create(XMLFile, settings))
                {
                        writer.WriteStartElement(rootNode);
                        writer.WriteAttributeString("displaymember", "name");
                        writer.WriteAttributeString("sortlist", SortList.ToString());
                        writer.WriteAttributeString("includeempty", IncludeEmpty.ToString());
                        writer.WriteAttributeString("includeall", IncludeAllCaption);
 
                        while ((line = sr.ReadLine()) != null)
                        {
                            if (!string.IsNullOrEmpty(line))
                            {
                                writer.WriteStartElement(childNode);
                                writer.WriteElementString("name", line);
                                writer.WriteEndElement();
                            }
                        }
                        writer.WriteEndElement();
                        writer.Flush();
                }
            }
        }

 

Now the method to take the newly created XML file and load it into a List<string> collection:

        public List<string> XMLToList(string XMLFile)
        {
            List<string> list = new List<string>();
 
            XmlTextReader xml = new XmlTextReader(XMLFile);
 
            string displayMember = string.Empty;
            bool sortList = false;
            string includeall = string.Empty;
            bool includeempty = false;
 
            while (xml.Read())
            {
                switch (xml.NodeType)
                {
                    case XmlNodeType.Element:
                        while (xml.MoveToNextAttribute())
                        {
                            if (xml.Name == "displaymember")
                                displayMember = xml.Value.ToString();
 
                            if (xml.Name == "sortlist")
                                sortList = bool.Parse(xml.Value.ToString());
 
                            if (xml.Name == "includeall")
                                includeall = xml.Value.ToString();
 
                            if (xml.Name == "includeempty")
                                includeempty = bool.Parse(xml.Value.ToString());
 
                        }
                        while (xml.Read())
                        {
                            if (xml.Name.ToString() == displayMember)
                            {
                                // read next node, the textnode to get value
                                xml.Read();
 
                                if (xml.NodeType != XmlNodeType.Whitespace)
                                    list.Add(xml.Value.ToString());
                            }
                        }
                        break;
                }
            }
 
 
            // If includeall is set then put it at the first of the list
            if (!string.IsNullOrEmpty(includeall))
                list.Insert(0, includeall);
 
            // If sort is set then sort the list before returning it from the method
            if (sortList)
                list.Sort(delegate(string s1, string s2) { return s1.CompareTo(s2); });
 
            // if includeempty is true then put a empty string at the first of the list
            if(includeempty)
                list.Insert(0,string.Empty);
 
            return list;
 
        }

Bit of info on the options:

  • The "displayMember" is the value of the element name to use for the string value (defaults to "name").
  • The "sortList" option is a bool that indicates if the list should be sorted before it is returned.
  • The "includeAll" option is a string value that will be put at the top of the list. Like "- All" or "* Select All *".
  • The "includeEmpty" option is a bool that (if set "true") sets the option to include empty DAT file entries in the string collection.

So put together like this:

        private void Form1_Load(object sender, EventArgs e)
        {
            string DATFile = @"C:\work\zipcodes.csv";
            string XMLFile = @"C:\work\zipcodes.xml";
            
            DATToXML(DATFile, XMLFile, true, false, string.Empty);
 
            Stopwatch sw = new Stopwatch();
 
            sw.Start();
 
            List<string> zipcodes = XMLToList(XMLFile);
 
            sw.Stop();
 
            TimeSpan ts = sw.Elapsed;
 
            this.labelXMLLoadTime.Text = "XML Load Time: " + 
                                         String.Format("{0:00}:{1:00}:{2:00}.{3:00}", ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds);
 
            sw.Start();
 
            this.comboBox1.DataSource = zipcodes;
 
            sw.Stop();
 
            ts = sw.Elapsed;
 
            this.labelComboboxLoadTime.Text = "Combobox Load Time: " + 
            String.Format("{0:00}:{1:00}:{2:00}.{3:00}", ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds);
 
            this.labelCount.Text = "Number Of Items Loaded: " + this.comboBox1.Items.Count.ToString("g");
            
        }

Awesome stuff. Super simple and very fast. I loaded up the entire US Zipcode collection in .223 seconds and then put that into a ComboBox in 2.9 seconds. I realize most people are not going to have 40,000 entries in a selectable ComboBox, my point is that it is zooming fast.

Enjoy!

Leave a Reply