将XML附加到文件中而无需从头开始写吗?[英] Append XML to file without writing it from scratch?

本文是小编为大家收集整理的关于将XML附加到文件中而无需从头开始写吗?的处理/解决方法,可以参考本文帮助大家快速定位并解决问题,中文翻译不准确的可切换到English标签页查看源文。

问题描述

linq-to-xml中附加XML文件的标准方法是读取它,修改内存文档,然后从头开始将整个文件写入.例如:

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

or 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);
    }
}

但是,在两种情况下,现有的XML文档都被加载到内存中,在内存中修改,然后从头开始写入到XML文件.对于小XML文件,这是可以的,但是对于具有数百或数千个节点的大文件,这似乎是一个非常效率的过程.有什么方法可以使XDocument(或类似XmlWriter之类的东西)附加到现有XML文档中的必要其他节点,而不是将其缩短并从头开始?

推荐答案

这完全取决于您需要添加其他元素的位置.当然,您可以实现删除关闭"</root>"标签的东西,写入其他元素,然后再次添加"</root>".但是,此类代码是为您的目的而高度优化的,您可能找不到库.

您的代码看起来像这样(快速而肮脏,没有输入检查,假设<root/>不存在):

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;
        }
    }
}

本文地址:https://www.itbaoku.cn/post/1556952.html

问题描述

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?

推荐答案

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;
        }
    }
}