1

The standard way to append an XML file in LINQ-to-XML is to read it in, modify the in-memory document, and write the whole file out from scratch. For example:

XDocument doc = XDocument.Load("pathToDoc.xml");
doc.Root.Add(new XElement(namespace + "anotherChild", new XAttribute("child-id", childId)));
doc.Save("pathToDoc.xml");

Or, with a FileStream:

using (FileStream fs = new FileStream("pathToDoc.xml", FileMode.Open, FileAccess.ReadWrite)) {
    XDocument doc = XDocument.Load(fs);
    doc.Root.Add(new XElement(namespace + "anotherChild", new XAttribute("child-id", childId)));

    fs.SetLength(0);
    using (var writer = new StreamWriter(fs, new UTF8Encoding(false))) {
        doc.Save(writer);
    }
}

However, in both cases, the existing XML document is being loaded into memory, modified in memory, and then written from scratch to the XML file. For small XML files this is OK, but for large files with hundreds or thousands of nodes this seems like a very inefficient process. Is there any way to make XDocument (or perhaps something like an XmlWriter) just append the necessary additional nodes to the existing XML document rather than blanking it out and starting from scratch?

Jez
  • 27,951
  • 32
  • 136
  • 233
  • 1
    Well formed xml has only one root tag. So you must read an existing file and add additional tags in the middle of the existing file. – jdweng Aug 06 '17 at 11:26
  • Yes, but one would hope a good serializer has the intelligence to seek to the right location and append. – Jez Aug 06 '17 at 11:38
  • An XML file is just a text file, and there's no easy way to insert in the middle of a text file. See [Adding a Line to the Middle of a File with .NET](https://stackoverflow.com/q/2044365/3744182). But since you're inserting immediately before the ending tag of the root element, see [Fastest way to add new node to end of an xml?](https://stackoverflow.com/q/849043/3744182) - which might be a duplicate. – dbc Aug 06 '17 at 19:44

1 Answers1

1

This totally depends on the position where you need to add the additional elements. Of course you can implement something that removes the closing "</root>" tag, writes additional elements and then adds the "</root>" again. However, such code is highly optimized for your purpose and you'll probably not find a library for it.

Your code could look like this (quick and dirty, without input checking, assuming that <root/> cannot exist):

using System.IO;
using System.Xml.Linq;

namespace XmlAddElementWithoutLoading
{
    class Program
    {
        static void Main()
        {
            var rootelement = "root";
            var doc = GetDocumentWithNewNodes(rootelement);
            var newNodes = GetXmlOfNewNodes(doc);

            using (var fs = new FileStream("pathToDoc.xml", FileMode.Open, FileAccess.ReadWrite))
            {
                using (var writer = new StreamWriter(fs))
                {
                    RemoveClosingRootNode(fs, rootelement);
                    writer.Write(newNodes);
                    writer.Write("</"+rootelement+">");
                }
            }
        }

        private static void RemoveClosingRootNode(FileStream fs, string rootelement)
        {
            fs.SetLength(fs.Length - ("</" + rootelement + ">").Length);
            fs.Seek(0, SeekOrigin.End);
        }

        private static string GetXmlOfNewNodes(XDocument doc)
        {
            var reader = doc.Root.CreateReader();
            reader.MoveToContent();
            return reader.ReadInnerXml();
        }

        private static XDocument GetDocumentWithNewNodes(string rootelement)
        {
            var doc = XDocument.Parse("<" + rootelement + "/>");
            var childId = "2";
            XNamespace ns = "namespace";
            doc.Root.Add(new XElement(ns + "anotherChild", new XAttribute("child-id", childId)));
            return doc;
        }
    }
}
Thomas Weller
  • 55,411
  • 20
  • 125
  • 222