how do I close this document that was called this way:
var xmlDoc = XDocument.Load(new XmlTextReader(Server.MapPath("Nc.xml")));
thanks
XmlTextReader implements IDisposable. In general, you should call IDisposable.Dispose() as soon as you no longer need the resource to allow the system to close open handles, etc.
The best use pattern for IDisposable is to use the using syntax, which will call IDisposable.Dispose() automatically in an implicit try..finally wrapper:
using (var reader = new XmlTextReader(Server.MapPath("Nc.xml")))
{
var xdoc = XDocument.Load(reader);
{ .. do xdoc work here .. }
} // reader disposed here
or if you want to keep the xdoc around a long time for other work but want to close the file as soon as possible, do it this way:
XDocument xdoc = null;
using (var reader = new XmlTextReader(Server.MapPath("Nc.xml")))
{
xdoc = XDocument.Load(reader);
} // reader disposed here
{ .. do xdoc work here .. }
Once the reader is done, it will close the what it has read automatically.
otherwise hang the reference out for GC by
xmlDoc = null;
which will tear down any internal open items.
Related
Cant seem to find a definite answer.
I am wondering if after initializing a XPathDocument with a StringReader and getting a XPathNavigator will I get errors if I use the Navigator after I dispose of the StringReader?
Here is an example.
XPathDocument doc = null;
XPathNavigator nav = null;
using (var reader = new StringReader(config))
{
doc = new XPathDocument(reader);
nav = doc.CreateNavigator();
nav.MoveToFirstChild();
}
var test = nav.SelectSingleNode("testNode");
If I continue to use this nav object will I get an error since the reader will have been disposed?
You will be able to continue to use nav. All StringReader does is read a config file, which gets passed to your XPathDocument doc. After that it's job is done, it's data has all been used.
I am trying to dispose the memory from an XmlDocument object
using (XmlNodeReader xnrAwards = new XmlNodeReader(ndListItems))
{
ndListItems.InnerXml = ndListItems.InnerXml.Replace("_x002e_", "_").Replace("ows_", "");
dsAward.ReadXml(xnrAwards, XmlReadMode.ReadSchema);
XmlDocument xdocAwards = new XmlDocument();
xdocAwards.LoadXml(ndListItems.OuterXml);
xdocAwards.Save(ABCListName + "_XML.xml");
}
Any idea on how to dispose the memory off this object as this is giving me an outofmemoryexception
Stop using XmlDocument if memory is a concern. It loads the entire document at once, which is causing you your issues.
Use instead a stream-based reader: XmlReader
This object chunks up the file into a buffer instead of loading the entire thing.
using (XmlReader reader = XmlReader.Create(file)) {
while (reader.Read()) {
//Do processing on each
}
}
Note this is a forward-only reader, and it's not as straight-forward to use as XmlDocument, but buffering your data will ensure you don't run into further memory exceptions.
If you're curious about the mechanism used for buffering, it's a yield return behind the scenes (which is actually compiled to a switch case if you want to get down to the nitty-gritty). Here is a blog post of someone doing something similar with a text file: http://jamesmccaffrey.wordpress.com/2011/02/04/processing-a-text-file-with-the-c-yield-return-pattern/
ref: http://msdn.microsoft.com/en-us/library/vstudio/system.xml.xmlreader
I have an XML file with no root. I cannot change this. I am trying to parse it, but XDocument.Load won't do it. I have tried to set ConformanceLevel.Fragment, but I still get an exception thrown. Does anyone have a solution to this?
I tried with XmlReader, but things are messed up and can't get it work right. XDocument.Load works great, but if I have a file with multiple roots, it doesn't.
XmlReader itself does support reading of xml fragment - i.e.
var settings = new XmlReaderSettings { ConformanceLevel = ConformanceLevel.Fragment };
using (var reader = XmlReader.Create("fragment.xml", settings))
{
// you can work with reader just fine
}
However XDocument.Load does not support reading of fragmented xml.
Quick and dirty way is to wrap the nodes under one virtual root before you invoke the XDocument.Parse. Like:
var fragments = File.ReadAllText("fragment.xml");
var myRootedXml = "<root>" + fragments + "</root>";
var doc = XDocument.Parse(myRootedXml);
This approach is limited to small xml files - as you have to read file into memory first; and concatenating large string means moving large objects in memory - which is best avoided.
If performance matters you should be reading nodes into XDocument one-by-one via XmlReader as explained in excellent #Martin-Honnen 's answer (https://stackoverflow.com/a/18203952/2440262)
If you use API that takes for granted that XmlReader iterates over valid xml, and performance matters, you can use joined-stream approach instead:
using (var jointStream = new MultiStream())
using (var openTagStream = new MemoryStream(Encoding.ASCII.GetBytes("<root>"), false))
using (var fileStream =
File.Open(#"fragment.xml", FileMode.Open, FileAccess.Read, FileShare.Read))
using (var closeTagStream = new MemoryStream(Encoding.ASCII.GetBytes("</root>"), false))
{
jointStream.AddStream(openTagStream);
jointStream.AddStream(fileStream);
jointStream.AddStream(closeTagStream);
using (var reader = XmlReader.Create(jointStream))
{
// now you can work with reader as if it is reading valid xml
}
}
MultiStream - see for example https://gist.github.com/svejdo1/b9165192d313ed0129a679c927379685
Note: XDocument loads the whole xml into memory. So don't use it for large files - instead use XmlReader for iteration and load just the crispy bits as XElement via XNode.ReadFrom(...)
The only in-memory tree representations in the .NET framework that can deal with fragments are the XmlDocumentFragment in .NET's DOM implementation so you would need to create an XmlDocument and a fragment with e.g.
XmlDocument doc = new XmlDocument();
XmlDocumentFragment frag = doc.CreateDocumentFragment();
frag.InnerXml = stringWithXml; // for instance
// frag.InnerXml = File.ReadAllText("fragment.xml");
or is XPathDocument where you can create one using an XmlReader with ConformanceLevel set to Fragment:
XPathDocument doc;
using (XmlReader xr =
XmlReader.Create("fragment.xml",
new XmlReaderSettings()
{
ConformanceLevel = ConformanceLevel.Fragment
}))
{
doc = new XPathDocument(xr);
}
// new create XPathNavigator for read out data e.g.
XPathNavigator nav = doc.CreateNavigator();
Obviously XPathNavigator is read-only.
If you want to use LINQ to XML then I agree with the suggestions made that you need to create an XElement as a wrapper. Instead of pulling in a string with the file contents you could however use XNode.ReadFrom with an XmlReader e.g.
public static class MyExtensions
{
public static IEnumerable<XNode> ParseFragment(XmlReader xr)
{
xr.MoveToContent();
XNode node;
while (!xr.EOF && (node = XNode.ReadFrom(xr)) != null)
{
yield return node;
}
}
}
then
XElement root = new XElement("root",
MyExtensions.ParseFragment(XmlReader.Create(
"fragment.xml",
new XmlReaderSettings() {
ConformanceLevel = ConformanceLevel.Fragment })));
That might work better and more efficiently than reading everything into a string.
If you wanted to use XmlDocument.Load() then you would need to wrap the content in a root node.
or you could try something like this...
while (xmlReader.Read())
{
if (xmlReader.NodeType == XmlNodeType.Element)
{
XmlDocument d = new XmlDocument();
d.CreateElement().InnerText = xmlReader.ReadOuterXml();
}
}
XML document cannot have more than one root elements. One root element is required. You may do one thing. Get all the fragment elements and wrap them into a root element and parse it with XDocument.
This would be the best and easiest approach that one could think of.
I have a really strange problem with XMLReader/XMLTextReader classes.
I have a simple file load:
public void First()
{
XmlTextReader reader = new XmlTextReader(#"C:\MyXMLFile.xml");
XmlReader readerToSerialize;
XmlReader readerToLoad;
DuplicateReaders(reader, out readerToSerialize, out readerToLoad);
XmlSerializer serializer = new XmlSerializer(typeof(XMLTree));
XmlFeed = (XMLDescriptor)serializer.Deserialize(readerToSerialize);
xmlDoc.Load(readerToLoad);
}
protected void DuplicateReaders(XmlTextReader xmlReader, out XmlReader cloneOne, out readerToLoad)
{
XmlDocument _XmlDocument = new XmlDocument();
MemoryStream _Stream = new MemoryStream();
_XmlDocument.Load((XmlTextReader)xmlReader);
_XmlDocument.Save(_Stream);
_Stream.Position = 0L;
cloneOne = XmlReader.Create(_Stream);
_Stream.Position = 0L;
cloneTwo = XmlReader.Create(_Stream);
}
The problem is that only one of the cloned elements read the whole file successully, the next one (xmlDoc.Load) fails always at the same place (Line 91, Character 37 with this xml file). If I directly assign to xmlDoc (i.e. clone the original element only once and asign it directly from the function):
public void First()
{
XmlTextReader reader = new XmlTextReader(#"C:\MyXMLFile.xml");
XmlReader readerToSerialize;
DuplicateReaders(reader, out readerToSerialize);
XmlSerializer serializer = new XmlSerializer(typeof(XMLTree));
XmlFeed = (XMLDescriptor)serializer.Deserialize(readerToSerialize);
}
protected void DuplicateReaders(XmlTextReader xmlReader, out XmlReader cloneOne)
{
XmlDocument _XmlDocument = new XmlDocument();
MemoryStream _Stream = new MemoryStream();
_XmlDocument.Load((XmlTextReader)xmlReader);
_XmlDocument.Save(_Stream);
_Stream.Position = 0L;
cloneOne = XmlReader.Create(_Stream);
_Stream.Position = 0L;
this.xmlDoc.Load(_Stream);
}
I still get the same error 91/37 (Unexpected EOF), but this time in the Serializer.
My initial problem was that if I use xmlDoc.Load(reader) the reader instance get destroyed and I can't serialize it later on. I found the Duplicate function on the MSDN forums, but it's still a no go. What I want to achieve is quite simple:
Use only one reader and get one XmlDocument and one Serialized Class. How hard can it be?
You need to close the first reader before you can use the duplicate.
reader.Close()
Your both cloneOne and cloneTwo use the same underlying memory stream.
use a different MemoryStream
cloneTwo = XmlReader.Create(new MemoryStream(_Stream.ToArray()));
Found much easier solution, instead of cloning the two readers, i just use create a second one from XmlDoc and use it to deserialize.
I have the following method GetData that creates a StreamReader from a file.
private void GetData(string file)
{
string filename = Path.GetFileNameWithoutExtension(file);
XmlDocument xmldoc = new XmlDocument();
using (StreamReader sr = new StreamReader(file))
{
Stream bs = sr.BaseStream;
Stream cl = mainParser.CleanMarkup(bs);
try
{
xmldoc = mainParser.LoadDocument(bs);
}
catch (XmlException ex)
{
// Exceptions are usually caused by non-compliant documents.
// These errors are not critical to the operation of this program.
Console.WriteLine(filename + " " + ex.Message);
}
}
Msdn msdnParser = new Msdn(xmldoc);
ListViewItem lvitem = new ListViewItem(filename);
lvitem.SubItems.Add(filename);
foreach (string item in msdnParser.Subitems)
{
lvitem.SubItems.Add(item);
}
listView.Items.Add(lvitem);
}
mainParser.LoadDocument(bs) calls the following:
public XmlDocument LoadDocument(Stream file)
{
XmlDocument xmldoc = new XmlDocument();
XmlReader xmlread = XmlReader.Create(file);
xmldoc.Load(xmlread);
return xmldoc;
}
StreamReader is disposed by GetData. Does this mean that I don't have to dispose of XmlReader since (I believe) this would dispose of its only unmanaged resource?
The best "rule of thumb" to work by is:
If something implements IDisposable, always wrap it in a using() block to ensure that any unmanaged resources it owns are disposed of correctly.
Relying on the fact that the current implementation of "something" disposes of an underlying resource is dangerous and it won't hurt to wrap everything in a using, just to be on the safe side =)
You're right, you don't have to dispose the reader. But in the code given, it wouldn't hurt either.
I would not put a using block inside LoadDocument() because it is designed so that it 'borrows' it's stream (it does not create it).
But there are arguments to Dispose the XmlReader anyway, just because it's IDisposable. I don't think there is a clear winner here because of the disputable design of the Reader (and Writer) family: They Dispose their baseStreams without clearly being the owner of those streams.