XML update attributes: add characters at the end and fail - c#

I'm trying to update the attributes of a xml. My problem is that when I update and later I read my xml I have an exception. I was looking for the problem and I don't know how but at the end of xml appears the last character of my general close tag. Sometimes the three or two characters last or only the character >. This problem don't occur always. Sometimes in the second time, others in fourth time, other in tenth times... I put a snippet bellow. thanks so much and sorry for my English.
PD: I don't use Linq
My XML
// At the end of xml file appears this fragment
<?xml version="1.0"?>
<MYPRINCIPALTAG>
<TAG DATE="01/01/01"></TAG>
</MYPRINCIPALTAG>AG>
More code ;-)
// Read XML
System.IO.FileStream fs = new System.IO.FileStream (path, System.IO.FileMode.Open, System.IO.FileAccess.Read);
XmlTextReader reader = new XmlTextReader (fs);
while (reader.Read ()) {
switch (reader.NodeType) {
case XmlNodeType.Element:
switch (reader.Name) {
case 'TAG':
string pubdate = reader.GetAttribute (0);
break;
}
break;
}
fs.Close();
public static XmlNode OpenXmlNode(string path, ref System.IO.FileStream fs, ref XmlDocument doc) {
fs = new System.IO.FileStream (path, System.IO.FileMode.Open, System.IO.FileAccess.ReadWrite);
doc = new XmlDocument ();
doc.Load (fs);
fs.Flush ();
fs.Close ();
return doc.DocumentElement;
}
public static void CloseXmlNode(string path, ref System.IO.FileStream fs, ref XmlDocument doc) {
fs = new System.IO.FileStream (path, System.IO.FileMode.Open, System.IO.FileAccess.ReadWrite);
doc.Save (fs);
fs.Flush ();
fs.Close ();
}
public static Boolean UpdateXML(string path, string id_tag) {
try {
System.IO.FileStream fs = null;
XmlDocument doc = new XmlDocument ();
XmlNode element = OpenXmlNode (path, ref fs, ref doc);
// Change Date
element.ChildNodes[0].Attributes.GetNamedItem ("date").Value = DateTime.Now.ToString ("dd/MM/yy");
for (int count = 1; count < doc.DocumentElement.ChildNodes.Count; count++) {
if ((doc.DocumentElement.ChildNodes[count].Attributes.GetNamedItem ("ID").Value).Equals (id_tag)) {
for (int i = 0; i < doc.DocumentElement.ChildNodes[count].ChildNodes[0].ChildNodes.Count; i++) {
doc.DocumentElement.ChildNodes[count].ChildNodes[0].ChildNodes[i].Attributes.GetNamedItem ("STATE").Value = "ok";
}
break;
}
}
CloseXmlNode (path, ref fs, ref doc);
return true;
} catch (Exception e) {
return false;
}
}

Try doing a fs.flush(); before fs.Close();
Like this
doc.Save(fs);
fs.Flush();
fs.Close();
Better still. The way that you have written your code (with static functions), you don't seem to be benefitting from using filestreams (IMO). I would change the functions OpenXmlNode() and CloseXmlNode() as follows:
public static XmlNode OpenXmlNode(string path, ref XmlDocument doc)
{
doc = new XmlDocument ();
doc.Load (path);
return doc.DocumentElement;
}
public static void CloseXmlNode(string path, ref XmlDocument doc)
{
doc.Save (path);
}

Related

Replace lowercase with uppercase in a filestream

I´m trying to replace lowercase characters to uppercase in a text, using a FileStream. I´m using this code, but it seems that WriteByte won´t overwrite the character. Could you help me out with that? thanks!
FileStream fs = new FileStream("./texto.txt", FileMode.OpenOrCreate, FileAccess.ReadWrite);
for (int i = 1; i <= fs.Length; i++)
{
if (char.IsLower(Convert.ToChar(fs.ReadByte())))
{
fs.Seek(-1, SeekOrigin.Current);
fs.WriteByte((byte)char.ToUpper(Convert.ToChar(fs.ReadByte())));
}
}
fs.Close();
The procedure has two issues:
fs.ReadByte() is called twice (as already noted #C.Evenhuis)
doesn't take in accounts the text encoding; for example if the file was UTF-8-BOM the update file would have at start three "dirty" chars (three BOM byte converted)
From the design point of view, would be better isolate the "business" rule.
Based on these considerations I propose this refactoring:
var fileToModify = "TextToUpdate.txt";
var fileEncoding = Encoding.UTF8;
var readingStream = new FileStream(fileToModify, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite);
var writingStream = new FileStream(fileToModify, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite);
var bw = new BinaryWriter(writingStream, fileEncoding);
foreach (var charToWrite in readingStream.ToUpperChars(fileEncoding))
{
bw.Write(charToWrite);
}
readingStream.Close();
writingStream.Close();
Following the extension method ToUpperChars that isolates the business rule:
public static IEnumerable<Char> ToUpperChars(this Stream stream, Encoding encoding)
{
var br = new BinaryReader(stream, encoding);
Boolean eof = false;
Char charToReturn = Char.MinValue;
while (!eof)
{
try
{
charToReturn = br.ReadChar();
if (Char.IsLower(charToReturn))
{
charToReturn = Char.ToUpper(charToReturn);
}
}
catch (System.IO.EndOfStreamException)
{
eof = true;
}
if (eof)
{
yield break;
}
else
{
yield return charToReturn;
}
}
}

xmlDocument is already used by another process

I've created a program that read and write values from an XML file. When I try to save the XML with xmlDoc.Save(path); it throws an exception because the file is already used by another process.
Read Method:
private void ReadXml()
{
//instantiate the xmlDocument
xmlDoc = new XmlDocument();
//declare an XmlNodeList
XmlNodeList xmlNode;
//create a FileStream to read the file
FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read);
//load the file
xmlDoc.Load(fs);
//get all the nodes from the file
xmlNode = xmlDoc.GetElementsByTagName("Drop");
//read all values frome the nodes and save it into variables
for (int i = 0; i <= xmlNode.Count - 1; i++)
{
string name= xmlNode[i].ChildNodes.Item(0).InnerText.Trim();
int DefaultMin= Convert.ToInt32(xmlNode[i].ChildNodes.Item(1).InnerText.Trim());
int DefaultMax = Convert.ToInt32(xmlNode[i].ChildNodes.Item(2).InnerText.Trim());
int min = Convert.ToInt32(xmlNode[i].ChildNodes.Item(3).InnerText.Trim());
int max= Convert.ToInt32(xmlNode[i].ChildNodes.Item(4).InnerText.Trim());
int line= Convert.ToInt32(xmlNode[i].ChildNodes.Item(5).InnerText.Trim());
string lineToChange = xmlNode[i].ChildNodes.Item(6).InnerText;
string filePath = xmlNode[i].ChildNodes.Item(7).InnerText.Trim();
//create the DropItem object
DropItem drop = new DropItem(name, DefaultMin, DefaultMax, line, lineToChange, installPath+filePath);
drop.MinValue = min;
drop.MaxValue = max;
//add the object to the list
drops.Add(drop);
}
}
Write Method:
public void WriteXml()
{
//declare a xmlNodeList
XmlNodeList xmlNode;
//create a FileStream to read the file
FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read);
//load the file
xmlDoc.Load(fs);
//get all the nodes from the file
xmlNode = xmlDoc.GetElementsByTagName("Drop");
//write the values in the xml
for (int i = 0; i <= drops.Count - 1; i++)
{
xmlNode[i].ChildNodes.Item(3).InnerText=drops[i].MinValue.ToString();
xmlNode[i].ChildNodes.Item(4).InnerText = drops[i].MaxValue.ToString();
}
//save the document
xmlDoc.Save(path);
}
I think I have to close the xmlDoc in the read method before saving it.
You are not closing the stream,
use Using statement in order to dispose and close the FileStream :
using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read))
{
//load the file
xmlDoc.Load(fs);
//get all the nodes from the file
xmlNode = xmlDoc.GetElementsByTagName("Drop");
}
Try adding using to the File Stream this should help guarantee that the file from the File Stream is not in use.
public void WriteXml()
{
//declare a xmlNodeList
XmlNodeList xmlNode;
//create a FileStream to read the file
using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read))
{
//load the file
xmlDoc.Load(fs);
//get all the nodes from the file
xmlNode = xmlDoc.GetElementsByTagName("Drop");
//write the values in the xml
for (int i = 0; i <= drops.Count - 1; i++)
{
xmlNode[i].ChildNodes.Item(3).InnerText=drops[i].MinValue.ToString();
xmlNode[i].ChildNodes.Item(4).InnerText = drops[i].MaxValue.ToString();
}
}
//save the document
xmlDoc.Save(path);
}
Try to Dispose the FileStream object at the end of ReadXml and WriteXml methods.

How to Convert Different Enities in a single XML string

I have Converted a the Different entities in a List and now I have a master list containing the many list of different models.
public static string xmlSerialize(List<Object> o)
{
XmlDocument xmlOut = new XmlDocument();
foreach(var i in o)
{
using (MemoryStream xmlStream = new MemoryStream())
{
//MemoryStream myMemStr = new MemoryStream();
XmlSerializer xmlSerializer = new XmlSerializer(i.GetType());
xmlSerializer.Serialize(xmlStream, i);
xmlStream.Position = 0;
xmlOut.Load(xmlStream);
}
}
return xmlOut.InnerXml;
}
The above method converts the multiple List into a XML. But this code Overwrites the previous XML string in every loop run.
Is There any solution to get the single XML for every entity.
IMHO - the simplest solution here is to load xml into temporary XmlDocument and then perform Import
Idea is the following
public static string xmlSerialize(List<Object> o)
{
XmlDocument xmlOut = new XmlDocument();
foreach(var i in o)
{
var tmpDoc = new XmlDocument();
using (MemoryStream xmlStream = new MemoryStream())
{
//MemoryStream myMemStr = new MemoryStream();
XmlSerializer xmlSerializer = new XmlSerializer(i.GetType());
xmlSerializer.Serialize(xmlStream, i);
xmlStream.Position = 0;
tmpDoc.Load(xmlStream);
}
var newNode = xmlOut.ImportNode(tmpDoc.DocumentElement.LastChild, true);
xmlOut.DocumentElement.AppendChild(newNode);
}
return xmlOut.InnerXml;
}

"Root element is missing" error but I have a root element

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

openxml-sdk - Creating word 2007 file with settings.xml

I am trying to generate a new Word document using the following code. The Word document gets generated without settings.xml. I need to have settings.xml in the word file. Any help would be appreciated.
public static byte[] GenerateNewDocument()
{
byte[] returnValue = null;
MemoryStream stream = null;
WordprocessingDocument wordDoc = null;
try
{
stream = new System.IO.MemoryStream();
wordDoc = WordprocessingDocument.Create(stream, WordprocessingDocumentType.Document);
}
catch
{
if (stream != null)
{
stream.Close();
}
throw;
}
using (wordDoc)
{
wordDoc.AddMainDocumentPart();
MainDocumentPart mainPart = wordDoc.MainDocumentPart;
mainPart.Document = new Document(new Body());
mainPart.Document.Save();
}
returnValue = stream.ToArray();
return returnValue;
}
You need to create your own DocumentSettingsPart and then insert it into the MainDocumentPart. So the settings part may look like this:
<w:settings xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math" xmlns:v="urn:schemas-microsoft-com:vm" xmlns:w10="urn:schemas-microsoft-com:office:word" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:sl="http://schemas.openxmlformats.org/schemaLibrary/2006/main">
<w:defaultTabStop w:val="475"/>
<w:compat>
<w:compatSetting w:name="compatibilityMode" w:uri="http://schemas.microsoft.com/office/word" w:val="14"/>
</w:compat>
</w:settings>
And then having that saved somewhere as "settings.xml", you could use code like this:
private static void AddSettingsToMainDocumentPart(MainDocumentPart part)
{
DocumentSettingsPart settingsPart = part.AddNewPart<DocumentSettingsPart>();
FileStream settingsTemplate = new FileStream("settings.xml", FileMode.Open, FileAccess.Read);
settingsPart.FeedData(settingsTemplate);
settingsPart.Settings.Save();
}

Categories

Resources