In C#, is there a way to add an XML node to a file on disk WITHOUT loading it first?
I have a v开发者_C百科ery basic XML structure/file on disk which is something like:
<root>
<data timestamp="dd-mm-yyyy" type="comment">kdkdkdk</data>
<data timestamp="dd-mm-yyyy" type="event">kdkdkdkgffgfdgf</data>
<data timestamp="dd-mm-yyyy" type="something">kddsdsfsdkdkdk</data>
</root>
The XML will be, as mentioned, in an external file on disk. As the file might grow fairly large (actually gets 'trimmed' every couple of weeks), I don't want to load the XML file first to add a new node...
Is there a way to add a new node like this? it can be just added to the top/bottom etc, as the process that actually uses the XML sorts it by timestamp anyway..
I'm guessing a crude way is to append the node as text.. however I think that would add the node AFTER the end tag??
Any ideas gratefully received.. David.
Not with any XML API or tool.
You could open the file as Text, find the Position of </root>
and start overwriting from there. And of course add the </root>
again.
A quick and dirty approach, this is not very robust:
- make sure you prep the initial file, there should be nothing (no whitespace) after the closing tag
- make sure you use ASCII or UTF8 encoding
====
string closeTag = "</root>";
int closeLength = Encoding.UTF8.GetBytes(closeTag).Length;
var fs = System.IO.File.Open(filename, System.IO.FileMode.Open);
fs.Position = fs.Length - closeLength;
var writer = new StreamWriter(fs); // after the Position is set
writer.WriteLine(newTag);
writer.Write(closeTag); // NOT WriteLine !!
writer.Close();
fs.Close();
And of course you know about using using() {}
blocks.
Somehow, you'll have to read the XML file, since you cannot just add a node to the document.
An Xml file can have only one root, so the new node will have to be added after the last data element, but, before the root closing tag. Otherwise the xml is not valid.
There are a a number of decent answers here already, however just to put another spin on this, are you sure this data needs to be stored as an XML file?
The data you are storing looks fairly simple (i.e a record with three fields, date, type and some string data. If you do not need any of the added benefits that XML provides (see here) then why not just use basic CSV? That way you can just keep appending to it just like a log file via File.AppendText("filename")
.
(Hell, it might even be possible to use something like log4net to manage the logging and any clean up housekeeping.)
Depends.
If you want to do it the "correct" way, you have to read the file and the XML in it. You can avoid loading it completely into memory by using an XmlReader class for example.
However, if you definitely absolutely know the text-layout and the encoding of the file, you can avoid reading and re-writing it completely by opening it as random-access file (FileStream), skip to the end (minus the "<root/>"), add the new entry there and write the "<root/>" again.
I can't see a way to do what you're up to without reading the full file, but as a workaround maybe you could treat the file as a plain text ie without the root node (it will be invalid XML). Then, you could just append new nodes to the file as plain text. And, since your XML parsing process loads the entire file anyway, it can add the root node before treating it as XML.
System.IO.FileInfo[] aryFi = di.GetFiles("*.xml");
foreach (System.IO.FileInfo fi in aryFi) {
System.Xml.XmlDocument xmlDocument = new System.Xml.XmlDocument();
xmlDocument.Load(fi.FullName);
XmlNode refelem = xmlDocument.LastChild;
XmlNode newElem = xmlDocument.CreateNode("element", "something", "");
newElem.InnerText = "sometext";
xmlDocument.InsertAfter(newElem, refelem);
}
I believe opening and inserting a node would be best option. Either way you would need to use IO, why not do it proper way?
For single file
System.Xml.XmlDocument xmlDocument = new System.Xml.XmlDocument();
xmlDocument.Load("file path");
XmlNode refelem = xmlDocument.LastChild;
XmlNode newElem = xmlDocument.CreateNode("element", "something", "");
newElem.InnerText = "sometext";
xmlDocument.InsertAfter(newElem, refelem);
If by not loading XML, you mean not building a DOM tree, VTD-XML is the only API that allows you to cut paste split or modify incrementally. Furthermore, because VTD-XML is memory efficent, you won't have to worry about the size of XML document. A similar post in Java is at How to update large XML file. Notice that vtd-xml is available in Java, C# , C and C++.
just use
string s = File.ReadAllText(path)
s = s.replace("</root>", newnode + "</root>")
File.WriteAllText(s, path)
精彩评论