Sunday, 21 May 2017

Xml Reader

XmlReader provides fast forward non cached access to xml data

<?xml version="1.0" encoding="utf-8" ?>
<people>
  <person firstname="john" lastname="doe">
    <contactdetails>
      <emailaddress>john@unknown.com</emailaddress>
    </contactdetails>
  </person>
  <person firstname="jane" lastname="doe">
    <contactdetails>
      <emailaddress>jane@unknown.com</emailaddress>
    </contactdetails>
  </person>
  <person firstname="pawel" lastname="chooch">
    <contactdetails>
      <emailaddress>pchooch@unknown.com</emailaddress>
    </contactdetails>
  </person>
</people>


Now if we wanted to ready this data we could use the xmlReader

static void Main(string[] args)
{
    var xmlRS = new XmlReaderSettings();
    xmlRS.IgnoreWhitespace = true;

    var path = @"../../data.xml";
    using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read))
    using (var xmlReader = XmlReader.Create(fs, xmlRS))
    {
        xmlReader.MoveToContent();
        xmlReader.ReadStartElement("people");

        for (int i = 0; i < 3; i++)
        {
            string firstName = xmlReader.GetAttribute("firstname");
            string lastName = xmlReader.GetAttribute("lastname");

            Console.WriteLine($"Person: {firstName} {lastName}");
            xmlReader.ReadStartElement("person");

            Console.WriteLine("ContactDetails");
            xmlReader.ReadStartElement("contactdetails");
            //Inside Contactdetails node
            string emailAddressJohn = xmlReader.ReadString();
            Console.WriteLine("Email address: {0}", emailAddressJohn);

            xmlReader.ReadEndElement();//finish with email Address
            xmlReader.ReadEndElement();//Finish with ContactDetails
            xmlReader.ReadEndElement();//Finish with Person
        }        
    }
}

Now with this working we can see how brittle it is, especially since you have to close the current node you're reading with the read end element method.

there are other slightly more complicated approaches to using the xmlReader, but regardless it's very easy to get lost in what you're doing.

static void Main(string[] args)
{
    var xmlRS = new XmlReaderSettings();
    xmlRS.IgnoreWhitespace = true;

    var path = @"../../data.xml";
    var nodes = new string[] { "emailaddress", "phonenumber" };
           
    using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read))
    using (var xmlReader = XmlReader.Create(fs, xmlRS)) {
        xmlReader.MoveToContent();
        while (xmlReader.Read())
            if (xmlReader.AttributeCount > 0)
            {
                Console.Write(xmlReader.GetAttribute("firstname") + " ");
                Console.WriteLine(xmlReader.GetAttribute("lastname"));
            }
            else if (nodes.Contains(xmlReader.Name))
                Console.Write($"\t {xmlReader.Name}:");
            else if (!String.IsNullOrEmpty(xmlReader.Value))
            {
                Console.WriteLine(xmlReader.Value);
                //read closing node for contact info
                xmlReader.Read();
            }
    }

}

the second example is a bit more robust, but also more complicated to follow, it takes a lot of care to leverage the xmlReader or xmlWriter however they are very efficient for reading and writing to xml files.