"Root element is missing" error but I have a root element - c#

If anyone can explain why I'm getting a "Root element is missing" error when my XML document (image attached) has a root element, they win a pony which fires lazers from its eyes.
Code:
if (ISF.FileExists("Players.xml"))
{
string xml;
using (IsolatedStorageFileStream rawStream = ISF.OpenFile("Players.xml", FileMode.Open))
{
StreamReader reader = new StreamReader(rawStream);
xml = reader.ReadToEnd();
XmlReaderSettings settings = new XmlReaderSettings { IgnoreComments = true, IgnoreWhitespace = true };
XmlReader xmlReader = XmlReader.Create(reader, settings);
while (xmlReader.Read())
{
switch (xmlReader.NodeType)
{
case XmlNodeType.Element:
switch (xmlReader.Name)
{
case "numberOfPlayers":
string nodeValue = xmlReader.ReadContentAsString();
int NODEVALUE = int.Parse(nodeValue);
MessageBox.Show(" " + NODEVALUE);
break;
}
break;
}
break;
}
reader.Close();
}
}

Your problem is due to this line:
xml = reader.ReadToEnd();
This positions the reader stream to the end so that when XmlReader.Create is executed, there is nothing left in the stream for it to read.
If you need the xml string to be populated, then you need to close and reopen the reader prior to XmlReader.Create. Otherwise, removing or commenting this line out will solve your problem.

Reset the base stream's position each time it is read if you want to read from the beginning as stated earlier, but you don't have to re-create the stream each time.
String xmlResource = Assembly.GetExecutingAssembly().GetName().Name + ".XML.IODeleter.xsd";
configXsd = new StreamReader(Assembly.GetExecutingAssembly().GetManifestResourceStream(xmlResource));
if (configXsd != null)
{
configXsd.BaseStream.Position = 0;
File.WriteAllText(apppath + #"\" + Assembly.GetExecutingAssembly().GetName().Name + ".XML.IODeleter.xsd", configXsd.ReadToEnd());
}

I ended up creating a quick little function to reference before each new XmlReader...
private void ResetStream()
{
/*
The point of this is simply to open the stream with a StreamReader object
and set the position of the stream to the beginning again.
*/
StreamReader reader = new StreamReader(m_stream);
if (reader != null)
{
reader.BaseStream.Position = 0;
}
}
So when I'm working in xml I call it right before I create my reader. I always have the same stream in memory and never recreate that.
ResetStream();
using (XmlReader reader = XmlReader.Create(m_stream)) { reader.Read(); }

Related

Reading xml file from specific line

this is my situation:
public static string ReadFromLine(int lineNumber, params string[] searchedAttribute)
{
//XmlTextReader reader = new XmlTextReader(xmlFilePath);
XmlReaderSettings settings = new XmlReaderSettings();
settings.LineNumberOffset = lineNumber;
settings.DtdProcessing = DtdProcessing.Parse;
FileStream fs = new FileStream(xmlFilePath, FileMode.OpenOrCreate, FileAccess.Read, FileShare.Read);
XmlReader reader = XmlReader.Create(fs, settings);
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element && reader.Name == searchedAttribute[0] || reader.Name == searchedAttribute[1])
{
if(reader.Name == searchedAttribute[0])
reader.MoveToAttribute(searchedAttribute[0]);
else
reader.MoveToAttribute(searchedAttribute[1]);
string currentAttributeValue = reader.ReadElementString();
return currentAttributeValue;
}
}
return "notFound";
}
In the method I want to read a xml file from line that is passed in the variable "linenumber".
Unfortunately, despite using my settings according to documentation, my reader starts at line 1 every time.
Appreciate any ideas how to solve it or another solutions.
Unfortunately, despite using my settings according to documentation...
There must be some misunderstanding here. An XmlReader always starts from the current position of the passed Stream, TextReader, etc.
All what LineNumberOffset does is just an adjustment of the reported line number (if there is any error during the processing, for example).
A possible (but not quite recommended) solution can be if you embed your FileStream into a StreamReader, read lineNumber of lines, and then create an XmlReader passing the StreamReader at the current position to the XmlReader.Create(TextReader,XmlReaderSettings) overload:
using var stream = File.OpenRead(xmlFilePath);
// skipping lineNumber of lines
var streamReader = new StreamReader(stream);
for (int i = 0; i < lineNumber; i++)
streamReader.ReadLine();
XmlReader xmlReader = XmlReader.Create(sreamReader, settings);
But in fact a more preferable (and formatting-proof) solution would be to read the whole XML into anXDocument and then navigate in the content by LINQ to XML.

c# how to end streamreader

I am doing a project Windows form for assignment in Uni, I want to search an already created text file to match a first name and last name then write some additional information if the name and last name exist. I have the code constructed and showing no errors, however when I run and attempt to add information I am being provided with an error which essentially says the next process (Streamreader writer can not access the file as it is already in use by another process) I assume this process is streamreader, I have tried to code it to stop reading to no avail. I am in my first 3 months learning coding and would appreciate some assistance if possible, I have put a snippet of my code below.
//check if there is a file with that name
if (File.Exists(sFile))
{
using (StreamReader sr = new StreamReader(sFile))
{
//while there is more data to read
while (sr.Peek() != -1)
{
//read first name and last name
sFirstName = sr.ReadLine();
sLastName = sr.ReadLine();
}
{
//does this name match?
if (sFirstName + sLastName == txtSearchName.Text)
sr.Close();
}
//Process write to file
using (StreamWriter sw = new StreamWriter(sFile, true))
{
sw.WriteLine("First Name:" + sFirstName);
sw.WriteLine("Last Name:" + sLastName);
sw.WriteLine("Gender:" + sGender);
}
You are using your writer inside the reader, using the same file.
A using disposes the object inside it, after the closing curly braces.
using(StreamReader reader = new StreamReader("foo")){
//... some stuff
using(Streamwriter writer = new StreamWriter("foo")){
}
}
Do it like so :
using(StreamReader reader = new StreamReader("foo")){
//... some stuff
}
using(Streamwriter writer = new StreamWriter("foo")){
}
As per my comment regarding the using statement.
Rearrange to the below. I've tested locally and it seems to work.
using (StreamReader sr = new StreamReader(sfile))
{
//while there is more data to read
while (sr.Peek() != -1)
{
//read first name and last name
sFirstName = sr.ReadLine();
sLastName = sr.ReadLine();
//does this name match?
if (sFirstName + sLastName == txtSearchName.Text)
break;
}
}
using (StreamWriter sw = new StreamWriter(sfile, true))
{
sw.WriteLine("First Name:" + sFirstName);
sw.WriteLine("Last Name:" + sLastName);
sw.WriteLine("Gender:" + sGender);
}
I've replaced the sr.Close with a break statement to exit out. Closing the reader causes the subsequent peek to error as it's closed.
Also, I've noticed that you are not setting gender? unless its set elsewhere.
hope that helps
You can use FileStream. It gives you many options to work with file:
var fileStream = new FileStream("FileName", FileMode.Open,
FileAccess.Write, FileShare.ReadWrite);
var fileStream = new FileStream("fileName", FileMode.Open,
FileAccess.ReadWrite, FileShare.ReadWrite);
I think this is what you want/need. You can't append to a file the way you are trying to do it. Instead you'll want to read your input file, and write a temp file as you are reading through. And, whenever your line matches your requirements, then you can write the line with your modifications.
string inputFile = "C:\\temp\\StreamWriterSample.txt";
string tempFile = "C:\\temp\\StreamWriterSampleTemp.txt";
using (StreamWriter sw = new StreamWriter(tempFile))//get a writer ready
{
using (StreamReader sr = new StreamReader(inputFile))//get a reader ready
{
string currentLine = string.Empty;
while ((currentLine = sr.ReadLine()) != null)
{
if (currentLine.Contains("Clients"))
{
sw.WriteLine(currentLine + " modified");
}
else
{
sw.WriteLine(currentLine);
}
}
}
}
//now lets crush the old file with the new file
File.Copy(tempFile, inputFile, true);

Load XML document in read-only access mode

How do I load an XML document in read-only mode?
I have an XML file which is opened in another process and I want to load it in my C# application as read-only.
XmlDocument.Load("file.xml") obviously throws this error:
Process cannot access a file because it is being used by another
process
So I tried stream reader too:
FileStream fs = new FileStream("file.xml", FileMode.Open, FileAccess.Read);
xmldoc.Load(fs);
But it also throws the same error.
So how can I access my XML Document in read-only mode?
Update
I tried XPathDocument and FileStream("file.xml", FileMode.Open, FileAccess.Read,FileShare.Read) as well. But neither of them solved the problem.
This class Shows read xml file in read only mode.
public List<string[]> GetRunningOrderOnTable(string tableNo, int shopid)
{
try
{
XmlDocument xmlDoc = new XmlDocument();
string xmlFilePath = #"C:\inetpub\wwwroot\ShopAPI\XmlData\RunningTables.xml";
//string xmlFilePath = HttpContext.Current.Server.MapPath("~/XmlData/RunningTables.xml");
// Option 1
// FileStream xmlFile = new FileStream(xmlFilePath, FileMode.Open,
//FileAccess.Read, FileShare.Read);
// xmlDoc.Load(xmlFile);
// Option 2
using (Stream s = File.OpenRead(xmlFilePath))
{
xmlDoc.Load(s);
}
//xmlDoc.Load(xmlFilePath);
List<string[]> st = new List<string[]>();
XmlNodeList userNodes = xmlDoc.SelectNodes("//Tables/Table");
if (userNodes != null)
{
foreach (XmlNode userNode in userNodes)
{
string tblNo = userNode.Attributes["No"].Value;
string sid = userNode.Attributes["ShopID"].Value;
if (tblNo == tableNo && sid == shopid.ToString())
{
string[] str = new string[5];
str[0] = userNode.Attributes["No"].Value;
str[1] = userNode.InnerText; // OrderNumber
str[2] = userNode.Attributes["OrderID"].Value;
str[3] = userNode.Attributes["OrderedOn"].Value;
str[4] = userNode.Attributes["TotalAmount"].Value;
st.Add(str);
}
}
}
else return new List<string[]>();
return st;
}
catch (Exception ex)
{
CustomLogging.Log("RunningTables.xml GetRunningOrderOnTable Error " + ex.StackTrace, LoggingType.XMLRead);
return new List<string[]>();
}
}
Given you've said that FileShare.Read doesn't work, it would appear that the other process has the file open for writing.
You could try opening it with FileAccess.Read and FileShare.ReadWrite, in which case you'll need to handle any errors that may occur if the other process does actually write to the file.
If that doesn't work, it's likely that the other process has it opened with FileShare.None, in which case there's nothing you can do about it. To check this, try opening the file with, say, Notepad.
But is it still possible for FileShare.ReadWrite to throws error if it works in most cases?
You will only get an error if another process has already opened the file using FileShare.None. You've confirmed that this isn't the case when it's open in Microsoft Word, so you should be OK.

Split large XML file after string found

What I have:
A large XML file # nearly 1 million lines worth of content. Example of content:
<etc35yh3 etc="numbers" etc234="a" etc345="date"><something><some more something></some more something></something></etc123>
<etc123 etc="numbers" etc234="a" etc345="date"><something><some more something></some more something></something></etc123>
<etc15y etc="numbers" etc234="a" etc345="date"><something><some more something></some more something></something></etc123>
^ repeat that by 900k or so lines (content changing of course)
What I need:
Search the XML file for "<etc123". Once found move (write) that line along with all lines below it to a separate XML file.
Would it be advisable to use a method such as File.ReadAllLines for the search portion? What would you all recommend for the writing portion. Line by line is not an option as far as I can tell as it would take much too long.
To quite literaly discard the content above your search string, I would not use File.ReadAllLines, as it would load the entire file into memory. Try File.Open and wrap it in a StreamReader. Loop on StreamReader.ReadLine, then start writing to a new StreamWriter, or do a byte copy on the underlying filestream.
An example of how to do so with StreamWriter/StreamReader alone is listed below.
//load the input file
//open with read and sharing
using (FileStream fsInput = new FileStream("input.txt",
FileMode.Open, FileAccess.Read, FileShare.Read))
{
//use streamreader to search for start
var srInput = new StreamReader(fsInput);
string searchString = "two";
string cSearch = null;
bool found = false;
while ((cSearch = srInput.ReadLine()) != null)
{
if (cSearch.StartsWith(searchString, StringComparison.CurrentCultureIgnoreCase)
{
found = true;
break;
}
}
if (!found)
throw new Exception("Searched string not found.");
//we have the data, write to a new file
using (StreamWriter sw = new StreamWriter(
new FileStream("out.txt", FileMode.OpenOrCreate, //create or overwrite
FileAccess.Write, FileShare.None))) // write only, no sharing
{
//write the line that we found in the search
sw.WriteLine(cSearch);
string cline = null;
while ((cline = srInput.ReadLine()) != null)
sw.WriteLine(cline);
}
}
//both files are closed and complete
You can copy with LINQ2XML
XElement doc=XElement.Load("yourXML.xml");
XDocument newDoc=new XDocument();
foreach(XElement elm in doc.DescendantsAndSelf("etc123"))
{
newDoc.Add(elm);
}
newDoc.Save("yourOutputXML.xml");
You could do one line at a time... Would not use read to end if checking contents of each line.
FileInfo file = new FileInfo("MyHugeXML.xml");
FileInfo outFile = new FileInfo("ResultFile.xml");
using(FileStream write = outFile.Create())
using(StreamReader sr = file.OpenRead())
{
bool foundit = false;
string line;
while((line = sr.ReadLine()) != null)
{
if(foundit)
{
write.WriteLine(line);
}
else if (line.Contains("<etc123"))
{
foundit = true;
}
}
}
Please note, this method may not produce valid XML, given your requirements.

XDocument.Save() unable to access file

Hoping I might be able to get some help with an infuriating problem which I can't seem to find a definitive answer for anywhere.
I am appending data to an xml document using an XDocument object, and then overwriting the existing file using xDoc.save(path), but for some reason the first time I run the code an error is thrown even though the file is not in use by any other process.
*"The process cannot access the file "C:\XXX\XXXX\Telemetry\2011_11_22.tlm because it is being used by another process."*
Subsequent iterations do not cause a problem.
Here is my code that I am using with try/catch removed for readability:-
XElement x = GenerateTelemetryNode(h); //Create a new element to append
if (File.Exists(path))
{
if (xDoc == null)
{
xDoc = XDocument.Load(new StreamReader(path));
}
}
else
{
xDoc = new XDocument();
xDoc.Add(new XElement("TSD"));
}
xDoc.Element("TSD").Add(x);
xmlPath = path;
xDoc.Save(path);
I'm sure there's a very simple explanation for it.
Many thanks in advance for any replies.
I would expect the problem is the StreamReader hasn't been disposed in which case it will still be attached to the document. I would suggest using wrapping the StreamReader creation in a using clause to ensure that is disposed of immediately after the document has been loaded:
if (xDoc == null)
{
using (var sr = new StreamReader(path))
{
xDoc = XDocument.Load(new StreamReader(sr));
}
}
Use the overload of XDocument.Load that takes a Uri (the file name) and not a stream.
if (File.Exists(path))
{
if (xDoc == null)
{
StreamReader stream = new StreamReader(path);
using (stream)
{
xDoc = XDocument.Load(stream);
}
}
}
else
{
xDoc = new XDocument();
xDoc.Add(new XElement("TSD"));
}
xDoc.Element("TSD").Add(x);
xmlPath = path;
xDoc.Save(path);
Can't test for the moment, but I'd suspect the StreamReader to be using it, especially if you first iteration only causes this Exception. Have you tried :
XElement x = GenerateTelemetryNode(h); //Create a new element to append
if (File.Exists(path))
{
if (xDoc == null)
{
StreamReader reader = new StreamReader(path);
xDoc = XDocument.Load(reader);
reader.Close();
reader.Dispose();
}
}
else
{
xDoc = new XDocument();
xDoc.Add(new XElement("TSD"));
}
xDoc.Element("TSD").Add(x);
xmlPath = path;
xDoc.Save(path);

Categories

Resources