0

I have a c# application, where I'm doing a data compare of two xml files inside a method called RevisionTree. I return a list of elements(XElement) from this method. From the BuildXml method, call that method and save the list as tree. Next I create an xml root XElement. I then loop over each element from tree and add specified descendants (status, msg, date) to the root element, each one of these are XElement. So i should see an xml doument with root, then a list of repeating xml. However, when i try to save the to the writer i get the following error.

Error

Exception thrown: 'System.InvalidOperationException' in System.Private.Xml.dll
An unhandled exception of type 'System.InvalidOperationException' occurred in System.Private.Xml.dll
Token StartDocument in state Document would result in an invalid XML document.

Code

    {
        IEnumerable<XElement>
        var tree = RevisionTree("C:\\Users\\Owner\\source\\repos\\SvnCore\\SvnCore\\old_logs.xml", "C:\\Users\\Owner\\source\\repos\\SvnCore\\SvnCore\\new_logs.xml");

        using (XmlWriter writer = XmlWriter.Create("C:\\Users\\Owner\\source\\repos\\SvnCore\\SvnCore\\Temp.xml", xmlSettings))
        {
            writer.WriteStartDocument();
            var root = new XElement("root");
            foreach (var node in tree)
            {
                root.Add(new XElement("id", node.FirstAttribute));
                root.Add(node.Descendants("status").FirstOrDefault());
                root.Add(node.Descendants("msg").FirstOrDefault());
                root.Add(node.Descendants("date").FirstOrDefault());
            }
            root.Save(writer); 
            writer.WriteEndElement();
            writer.WriteEndDocument();
        }
        return true; 
    }

enter image description here

AJ_
  • 3,787
  • 10
  • 47
  • 82
  • 2
    Try `root.WriteTo(writer)`. (Alternatively, leave out your manual writing of the document/element nodes -- `Save` should already take care of it, which is why you're getting the error.) – Jeroen Mostert Oct 23 '18 at 13:22
  • @JeroenMostert, you are correct. Please create an answer, ( maybe elaborate why i should not be using save ?), ill give mark it as the correct solution. – AJ_ Oct 24 '18 at 02:26

1 Answers1

1

XElement.Save produces an entire document on its own -- you need XElement.WriteTo, which does not. So either (simplified):

var sb = new StringBuilder();
using (var sw = new StringWriter(sb))
using (XmlWriter writer = XmlWriter.Create(sw)) {
    var root = new XElement("root");
    root.Add(new XElement("id", "1"));
    root.Save(writer);  // no DocumentStart, no ElementStart
}
<?xml version="1.0" encoding="utf-16"?><root><id>1</id></root>

or (if you wanted to write multiple elements, or for some other reason want to control the document node yourself):

using (XmlWriter writer = XmlWriter.Create(sw)) {
    writer.WriteStartDocument();
    writer.WriteStartElement("root");
    var notRoot = new XElement("notRoot");
    notRoot.Add(new XElement("id", "1"));
    notRoot.WriteTo(writer); 
    notRoot.WriteTo(writer);
}
<?xml version="1.0" encoding="utf-16"?><root><notRoot><id>1</id></notRoot><notRoot><id>1</id></notRoot></root>

Note that I'm omitting the End calls, since the XmlWriter will take care of that implicitly.

If you aren't doing anything interesting with the xmlSettings, the whole thing is even simpler since XElement.Save has an overload that accepts a file name directly, so you don't need an XmlWriter at all.

Jeroen Mostert
  • 27,176
  • 2
  • 52
  • 85