Search code examples
c#xmlxmlreader

Using XmlReader how do I read past an empty element?


I have written an Xml file using XmlWriter. Here is the code:

public void WriteXml(XmlWriter writer)
        {
            writer.WriteStartElement("element1");
            writer.WriteAttributeString("attrs1", Attrs1);
            writer.WriteAttributeString("attrs2", Attrs2);

            if (!string.IsNullOrWhiteSpace("element2"))
            {
                writer.WriteStartElement("element2");
                writer.WriteCData(Element2);
                writer.WriteEndElement();
            }
            if (!string.IsNullOrWhiteSpace("element3"))
            {
                writer.WriteStartElement("element3");
                writer.WriteCData(Element3);
                writer.WriteEndElement();
            }
            Element4.WriteXml(writer);
            Element5.WriteXml(writer);

            writer.WriteEndElement();            
        }

As you can see, element4 calls a method named "WriteXml()". Here is that method:

public void WriteXml(XmlWriter writer)
        {
            if (m_PropertyValueList.Count > 0)
            {
                writer.WriteStartElement("element4");

                foreach (var p in m_PropertyValueList)
                {
                    if (p.CurrentValue != null)
                    {
                        writer.WriteStartElement("property");
                        writer.WriteAttributeString("name", p.PropertyName);
                        writer.WriteAttributeString("value", p.CurrentValue.ToString());
                        writer.WriteEndElement();
                    }
                }    
                writer.WriteEndElement();
            }
        }

Notice that it checks for p.CurrentValue != null. If it is null then this element should be written as an empty element. In this case, p.CurrentValue is null so I do want an empty element. You will see in the Xml output below that is writing this element as . To me it looks like it is properly writing an empty element, PLEASE CORRECT ME IF I'M WRONG HERE.

The Xml file output looks like this:

<?xml version="1.0" encoding="UTF-8"?>

-<element>   
    -<element 1 attrs1="Some Info" attrs2="More Info">   
        -<element2>    
            +<![CDATA[]]>
        </element2>   
        -<element3>    
            -<![CDATA[ ]]>
        </element3>    
        <element4/>    
    </element1>    
</element>

Then I create a method for reading the Xml file back in. This is where it breaks. It does just fine reading element, element1, element2 and element3. However, when it gets to element4, I start having issues. I will explain below.

Here is the XmlReader method:

public void ReadXml(XmlReader reader)
        {
            if (reader.IsStartElement("element1"))
            {
                //
                // Get the values of all the <element1> attributes
                //
                Attrs1= reader.GetAttribute("attrs1");
                Attrs2 = reader.GetAttribute("attrs2");

                //
                // Read past <element1>
                //
                reader.Read();

                while (true)
                {
                    if (reader.IsStartElement("element2"))
                    {
                        Description = reader.ReadElementContentAsString();
                    }
                    else if (reader.IsStartElement("element3"))
                    {
                        Source = reader.ReadElementContentAsString();
                    }
                    else if (reader.IsStartElement("element4")) && (!reader.IsEmptyElement))
                    {
                        Element4.ReadXml(reader);
                    }
                    else if ((reader.IsStartElement("element5")) && (!reader.IsEmptyElement))
                    {
                        Element5.ReadXml(reader);
                    }
                    else
                    {
                        reader.MoveToContent();
                        reader.ReadEndElement();
                        break;
                    }
                }
            }
            else
            {
                throw new XmlException("Expected <element1> element was not present");
            }
        }

Notice that if element4 IsStartElement and if it's not an empty element it will call Element4.ReadXml(). Here is that method:

public void ReadXml(XmlReader reader)
        {
            if ((reader.IsStartElement("element4")) && (!reader.IsEmptyElement))
            {
                reader.Read();

                while (reader.IsStartElement("property"))
                {
                    string propertyName = reader.GetAttribute("name");
                    string propertyValue = reader.GetAttribute("value");
                    SetValue(propertyName, propertyValue);
                    reader.Read();
                }

                //
                //  Read </element4>
                //
                reader.MoveToContent();
                reader.ReadEndElement();
            }            
        }

But since element4 IS empty this method never gets called. So it will continue to the else{} statement and break when it gets to reader.ReadEndElement(); There error message I get is:

"Message: TestMethod myTestMethodName threw exception: System.Xml.XmlException 'Element' is an invalid XmlNodeType. Line 8, position 6."  

So my questions are:

  1. Is this because the element is empty? If so, how do I close or read past an empty element?
  2. Where I am reading element4 if it's start element and if it's not empty....If I remove (!reader.IsEmptyElement) I get an infinite loop. It continues to call Element4.ReadXml(reader). Why is this?
  3. How to I fix and continue past this so that I can read the rest of the elements.

Solution

  • Where I read my elements from my xml file I needed to add another else if condition to handle if the element is start element and if the element IS empty. I was only checking if it wasn't empty. So it didn't know how to handle it being empty. Then when those conditions were met I simply had to do reader.Read() to tell the app to read the element as is <element4/>.

    Here is what it looks like:

    else if (reader.IsStartElement("element4")) && (reader.IsEmptyElement))
        {
            reader.Read();
        }