save xml file using streamwriter - c#

I am trying to find a way to save data back to an xml file with an encoding "iso-8859-7".
firstly am loading the xml using
public XmlDocument LoadDocument(String x)
{
XmlDocument document = new XmlDocument();
StreamReader stream = new StreamReader(xml, Encoding.GetEncoding("iso-8859-7"));
document.Load(stream);
return (document);
}
to load attributes inside form controls and then when the save button is clicked
private void savebtn_Click(object sender, EventArgs e)
{
XmlNodeList attributes = commonMethods.LoadDocument(xml).DocumentElement.SelectNodes("//Class[#Name='" + classname + "']/Property[#Id='" + id + "']/attribute::*");
for (int x = 0; x < attributes.Count; )
{
foreach (Control ctr in table1.Controls)
{
if (ctr is TextBox)
{
if (ctr.Text == attributes[x].Value.ToString()) { x++; }
else
{
attributes[x].Value = ctr.Text; commonMethods.SaveDocument(xml);
x++;
}
}
else if (ctr is ComboBox)
{
if (((ComboBox)ctr).Text == attributes[x].Value) { x++; }
else
{
attributes[x].Value = ((ComboBox)ctr).Text; commonMethods.SaveDocument(xml);
x++;
}
}
}
}
}
it saves the changes back to the xml file. I used to save it without using xmlwriter like xmldoc.Save("sample.xml) but because of some characters inside the file I had to use a different approach like.
public XmlDocument SaveDocument(String x)
{
XmlDocument document = new XmlDocument();
StreamWriter stream = new StreamWriter(x,false,Encoding.GetEncoding("iso-8859-7"));
document.Save(xml);
return (document);
}
The problem is when I compile it says "xml is used by another process" and it fails.
System.IO.IOException

You've got this exception because the file is still opened by the StreamReader that is pending for finalization (garbage collection).
You should always dispose your streams (and reader / writers) to release the file handle as soon as possible.
public XmlDocument LoadDocument(String path)
{
XmlDocument document = new XmlDocument();
using (StreamReader stream = new StreamReader(path, Encoding.GetEncoding("iso-8859-7")))
{
document.Load(stream);
}
return (document);
}
public XmlDocument SaveDocument(XmlDocument document, String path)
{
using (StreamWriter stream = new StreamWriter(path,false,Encoding.GetEncoding("iso-8859-7")))
{
document.Save(stream);
}
return (document);
}
private void savebtn_Click(object sender, EventArgs e)
{
var doc = commonMethods.LoadDocument(xml);
XmlNodeList attributes = doc.DocumentElement.SelectNodes("//Class[#Name='" + classname + "']/Property[#Id='" + id + "']/attribute::*");
for (int x = 0; x < attributes.Count; )
{
foreach (Control ctr in table1.Controls)
{
if (ctr is TextBox)
{
if (ctr.Text == attributes[x].Value.ToString()) { x++; }
else
{
attributes[x].Value = ctr.Text; commonMethods.SaveDocument(doc, xml);
x++;
}
}
else if (ctr is ComboBox)
{
if (((ComboBox)ctr).Text == attributes[x].Value) { x++; }
else
{
attributes[x].Value = ((ComboBox)ctr).Text; commonMethods.SaveDocument(doc, xml);
x++;
}
}
}
}
}

you didn't dispose the StreamReader object after loading the document
add the using statement:
public XmlDocument LoadDocument(String x)
{
XmlDocument document = new XmlDocument();
using (StreamReader stream = new StreamReader(xml, Encoding.GetEncoding("iso-8859-7")))
{
document.Load(stream);
}
return (document);
}
reference: http://msdn.microsoft.com/en-us/library/system.io.streamreader(v=vs.110).aspx

Related

XML Serialization leaves file blank after restart

We have a problem where our industrial equipments software's .XML settings files become blank, yet they still have the correct number of bytes.
I have a feeling it might be caused by the way the customers are shutting down the PC as it tends to happen after they've down a shutdown, isolate, and boot. The way I save the files is,
Serialize to %temp% file
Validate that the newly created file starts with <?xml
If the /backup folders version of the file is older than a day, copy the existing file to the /backup folder
Copy new file to overwrite existing file.
I thought maybe it's related to encoding, disk caching, Windows Update, or Windows Recovery.
Looking for ideas as I've spent two years chasing down why this is happening.
As per request, here is the code.
public static bool SerializeObjXml(object Object2Serialize, string FilePath, Type type, bool gzip = false)
{
if (!Path.IsPathRooted(FilePath))
FilePath = Path.Combine(ApplicationDir, FilePath);
bool isSuccess = false;
var tmpFile = Path.GetTempFileName();
try
{
for (int i = 0; i < 3; i++)
{
try
{
Directory.CreateDirectory(Path.GetDirectoryName(FilePath));
if (gzip)
{
using (var ms = new MemoryStream())
{
XmlSerializer bf = new XmlSerializer(type);
bf.Serialize(ms, Object2Serialize);
ms.Position = 0;
using (var fileStream = new BinaryWriter(File.Open(tmpFile, FileMode.Create)))
{
using (GZipStream gzipStream = new GZipStream(fileStream.BaseStream, CompressionMode.Compress))
{
byte[] buffer = new byte[4096];
int numRead;
while ((numRead = ms.Read(buffer, 0, buffer.Length)) != 0)
{
gzipStream.Write(buffer, 0, numRead);
}
}
}
}
if (!FileChecker.isGZip(tmpFile))
throw new XmlException("Failed to write valid XML file " + FilePath);
}
else
{
using (var fs = new StreamWriter(File.Open(tmpFile, FileMode.Create), Encoding.UTF8))
{
XmlSerializer bf = new XmlSerializer(type);
bf.Serialize(fs, Object2Serialize);
}
if (!FileChecker.isXML(tmpFile))
throw new XmlException("Failed to write valid XML file " + FilePath);
}
isSuccess = true;
return true;
}
catch (XmlException)
{
return false;
}
catch (System.IO.DriveNotFoundException) { continue; }
catch (System.IO.DirectoryNotFoundException) { continue; }
catch (System.IO.FileNotFoundException) { continue; }
catch (System.IO.IOException) { continue; }
}
}
finally
{
if (isSuccess)
{
lock (FilePath)
{
try
{
//Delete existing .bak file
if (File.Exists(FilePath + ".bak"))
{
File.SetAttributes(FilePath + ".bak", FileAttributes.Normal);
File.Delete(FilePath + ".bak");
}
}
catch { }
try
{
//Make copy of file as .bak
if (File.Exists(FilePath))
{
File.SetAttributes(FilePath, FileAttributes.Normal);
File.Copy(FilePath, FilePath + ".bak", true);
}
}
catch { }
try
{
//Copy the temp file to the target
File.Copy(tmpFile, FilePath, true);
//Delete .bak file if no error
if (File.Exists(FilePath + ".bak"))
File.Delete(FilePath + ".bak");
}
catch { }
}
}
try
{
//Delete the %temp% file
if (File.Exists(tmpFile))
File.Delete(tmpFile);
}
catch { }
}
return false;
}
public static class FileChecker
{
const string gzipSig = "1F-8B-08";
static string xmlSig = "EF-BB-BF";// <?x";
public static bool isGZip(string filepath)
{
return FileChecker.CheckSignature(filepath, (3, gzipSig)) != null;
}
public static bool isXML(string filepath)
{
return FileChecker.CheckSignature(filepath, (3, xmlSig)) != null;
}
public static bool isGZipOrXML(string filepath, out bool isGZip, out bool isXML)
{
var sig = FileChecker.CheckSignature(filepath, (3, gzipSig), (3, xmlSig));
isXML = (sig == xmlSig);
isGZip = (sig == gzipSig);
return isXML || isGZip;
}
public static string CheckSignature(string filepath, params (int signatureSize, string expectedSignature)[] pairs)
{
if (String.IsNullOrEmpty(filepath))
throw new ArgumentException("Must specify a filepath");
if (String.IsNullOrEmpty(pairs[0].expectedSignature))
throw new ArgumentException("Must specify a value for the expected file signature");
int signatureSize = 0;
foreach (var pair in pairs)
if (pair.signatureSize > signatureSize)
signatureSize = pair.signatureSize;
using (FileStream fs = new FileStream(filepath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
if (fs.Length < signatureSize)
return null;
byte[] signature = new byte[signatureSize];
int bytesRequired = signatureSize;
int index = 0;
while (bytesRequired > 0)
{
int bytesRead = fs.Read(signature, index, bytesRequired);
bytesRequired -= bytesRead;
index += bytesRead;
}
foreach (var pair in pairs)
{
string actualSignature = BitConverter.ToString(signature, 0, pair.signatureSize);
if (actualSignature == pair.expectedSignature)
return actualSignature;
}
}
return null;
}
}
Using the operating system's move or copy file to overwrite an existing file is an atomic operation meaning the it wholly succeeds or doesn't and doesn't overlap other file operations.
Therefore what you have should work if that is how you are achieving step 4.
Copy new file to overwrite existing file.
If instead you are blanking out the existing file and re-writing the data I suspect that could be the the point of failure..
The issues while file space is being allocated the write is not occurring during shutdown, which leaves you when a file with bytes allocated without the data being flushed to disk.
During the OS shutdown, likely a ThreadAbortException is raised which triggers your finally block.
You can attempt to reproduce by calling Process.Start("shutdown", "-a") before your return statement but after you have set success = true.
I would suggest simplifying your code and have everything run inside of your try {} statement. This removes the possibility of having a state where success = true before your attempted your write to disk, which is then triggered in a finally statement trigged by a windows shutdown.
public static bool SerializeObjXml(
object Object2Serialize,
string FilePath,
Type type,
bool gzip = false)
{
if (!Path.IsPathRooted(FilePath))
FilePath = Path.Combine(ApplicationDir, FilePath);
Directory.CreateDirectory(FilePath);
for (int i = 0; i < 3; i++)
{
try
{
var tempFi = SerializeToXmlFile(Object2Serialize, type, gzip);
var fi = new FileInfo(FilePath);
if (fi.Exists)
fi.CopyTo(fi.FullName + ".bak", true);
tempFi.CopyTo(fi.FullName, true);
tempFi.Delete();
return true;
}
catch (Exception ex)
{
string message = $"[{DateTime.Now}] Error serializing file {FilePath}. {ex}";
File.WriteAllText(FilePath + ".log", message);
}
}
return false;
}
As a side note, you can simply use [Stream.CopyTo][1] and write directly to your temp file, without the need for intermediary streams or for manual buffer/byte read/write operations:
private static FileInfo SerializeToXmlFile(
object Object2Serialize,
Type type,
bool gzip)
{
var tmpFile = Path.GetTempFileName();
var tempFi = new FileInfo(tmpFile);
if (!gzip)
{
using (var fs = File.Open(tmpFile, FileMode.Create))
(new XmlSerializer(type)).Serialize(fs, Object2Serialize);
if (!FileChecker.isXML(tmpFile))
throw new Exception($"Failed to write valid XML file: {tmpFile}");
}
else
{
using (var fs = File.Open(tmpFile, FileMode.CreateNew))
using (var gz = new GZipStream(fs, CompressionMode.Compress))
(new XmlSerializer(type)).Serialize(fs, Object2Serialize);
if (!FileChecker.isGZip(tmpFile))
throw new Exception($"Failed to write valid XML gz file: {tmpFile}");
}
return tempFi;
}

XML ReadLine to txt file

I have a problem with saving data from XML URL nodes, using XMLReader, to a text file. Can you please help me out? I don't know how to do it.
Here is the code:
namespace XMLdemo2
{
class Program
{
static void Main(string[] args)
{
// Start with XmlReader object
String URLString = "https://www.shortcut.lv/xmls/tiesraide/ltv1.xml";
XmlTextReader reader = new XmlTextReader(URLString);
{
while (reader.Read())
{
if (reader.IsStartElement())
{
switch (reader.Name.ToString())
{
case "auth_token":
Console.WriteLine("Tokens IR : " + reader.ReadString());
break;
}
//Console.WriteLine("");
}
}
Console.ReadKey();
}
}
}
}
You can try something easier like this (if it's only one line you want to read)
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load("https://www.shortcut.lv/xmls/tiesraide/ltv1.xml");
XmlNode authTokenNode = xmlDoc.SelectSingleNode("//auth_token");
if(authTokenNode != null)
Console.WriteLine(authTokenNode.InnerText);
If it is multiple lines
XmlDocument xmlDoc = new XmlDocument();
XmlNodeList itemNodes = xmlDoc.SelectNodes("//auth_token");
foreach(XmlNode itemNode in itemNodes)
{
if((itemNode != null)
Console.WriteLine(itemNode.InnerText);
}

XmlDoc throws the process cannot access the file from tome to time?

I have the following code for writing XML file based on datacontracts
public static void LogDataContractToFile(string XMLStringToLog, string filePathAndName)
{
//String documentPath = string.Empty;
String xmlObject = string.Empty;
FileInfo fileinfo;
XmlDocumentFragment xmlDocumentFragment;
XmlTextWriter xmlWriter;
XmlDocument xmlDocument = null;
lock (LogDataContractToFileLock)
{
filePathAndName = filePathAndName.ToLower();
while (_workingWithFile.Contains(filePathAndName))
Thread.Sleep(1000);
_workingWithFile.Add(filePathAndName.ToLower());
try
{
#region Create XMLFile
fileinfo = new FileInfo(filePathAndName);
if (!fileinfo.Exists)
{
DirectoryInfo info = new DirectoryInfo(fileinfo.DirectoryName);
if (info.Exists == false)
info.Create();
using (xmlWriter = new XmlTextWriter(filePathAndName, System.Text.Encoding.UTF8))
{
xmlWriter.Formatting = Formatting.Indented;
xmlWriter.WriteStartDocument();
xmlWriter.WriteStartElement("root");
xmlWriter.WriteStartElement("objects");
xmlWriter.WriteEndElement();
xmlWriter.WriteEndElement();
xmlWriter.WriteEndDocument();
xmlWriter.Close();
}
}
else
{
//Se så att filen är 50 MB eller mindre
while (fileinfo.Length > 52428800)
{
xmlDocument = new XmlDocument();
xmlDocument.Load(filePathAndName);
xmlDocument.RemoveChild(xmlDocument.LastChild);
xmlDocument.Save(filePathAndName);
xmlDocument = null;
}
}
#endregion
xmlObject = XMLStringToLog;
//Open document
xmlDocument = new XmlDocument();
xmlDocument.Load(filePathAndName);
//Create a new fragment in current document
xmlDocumentFragment = xmlDocument.CreateDocumentFragment();
xmlDocumentFragment.InnerXml = xmlObject;
//Add new fragment after the first child
xmlDocument.DocumentElement.InsertBefore(xmlDocumentFragment, xmlDocument.DocumentElement.FirstChild);
xmlDocument.Save(filePathAndName);
xmlDocument = null;
}
finally
{
_workingWithFile.Remove(filePathAndName.ToLower());
}
}
}
The problem is that from time to time I get a The process cannot access the file exception? XmlDocument do not have any dispose so I can´t use using. How should this be handled properly?
Note that Im stuck on .NET 4.0.
To safely check for an element and add it if it doesn't exist you should use a ConcurrentDictionary:
private readonly ConcurrentDictionary<string,bool> _workingWithFile = new ConcurrentDictionary<string,bool>();
public static void LogDataContractToFile(string XMLStringToLog, string filePathAndName)
{
...
lock (LogDataContractToFileLock)
{
...
while(!_workingWithFile.TryAdd(filePathAndName, true))
{
Thread.Sleep(1000);
}
...
try
{
...
}
finally
{
//Perhaps check the result here.
bool result;
_workingWithFile.TryRemove(filePathAndName, out result);
}
}
}

How to store more then roots in xml file using c#

i am created one xml file name is data.xml in this xml file i am storing this information
like bellow
<products>
<cloths Id="1">
<ClothName>Sharts</ClothName>
<Size>40</Size>
<Price>1050</Price>
<Amount>1000000</Amount>
<images>c:\users\venkateshg\documents\visual studio 2010\Projects\WebApplication1\WebApplication1\ImgUpload\Tulips.jpg</images>
</cloths>
</products>
This is a C# Code i devloped
XmlDocument XDoc = new XmlDocument(); // root
XmlElement XElemRoot = XDoc.CreateElement("products");
XDoc.AppendChild(XElemRoot);
XmlElement XCloths = XDoc.CreateElement("cloths");
DataSet ds = new DataSet();
ds.ReadXml(Server.MapPath("data.xml"));
int idval ;
if (ds.Tables[0].Rows.Count > 1)
{
idval = ds.Tables[0].Columns[5][0];
if (idval == 0)
{
idval = 1;
}
else
{
idval += 1;
}
}
else
{
idval = 1;
}
XCloths.SetAttribute("Id",idval.ToString());
XElemRoot.AppendChild(XCloths);
XmlElement XClothName = XDoc.CreateElement("ClothName");
XClothName.InnerText = TextBox1.Text;
XCloths.AppendChild(XClothName);
XmlElement XSize = XDoc.CreateElement("Size");
XSize.InnerText = TextBox2.Text; ;
XCloths.AppendChild(XSize);
XmlElement XPrice = XDoc.CreateElement("Price");
XPrice.InnerText = TextBox3.Text;
XCloths.AppendChild(XPrice);
XmlElement XAmount = XDoc.CreateElement("Amount");
XAmount.InnerText = "1000000";
XCloths.AppendChild(XAmount);
FileUpload1.ToolTip = "Select Image For Upload...";
string ext = System.IO.Path.GetExtension(this.FileUpload1.PostedFile.FileName);
if (FileUpload1.HasFile)
{
//Check File is available in Fileupload control and then upload to server path
string fname = FileUpload1.FileName;
//spath = #"~\ImgUpload\" + FileUpload.FileName;
string fpath = Server.MapPath("ImgUpload");
fpath = fpath + #"\" + FileUpload1.FileName;
string getext = System.IO.Path.GetExtension(FileUpload1.PostedFile.FileName);
string filename = System.IO.Path.GetFileNameWithoutExtension(FileUpload1.PostedFile.FileName);
string strFilePath = filename + DateTime.Now.ToString("yyyyMMdd") + getext;
if (getext != ".JPEG" && getext != ".jpeg" && getext != ".JPG" && getext != ".jpg" && getext != ".png" && getext != ".tif" && getext != ".tiff")
{
Page.ClientScript.RegisterStartupScript(typeof(Page), "successfull", "alert('Please upload only jpeg, jpg,png,tif,tiff'); window.location = 'Default.aspx';", true);
}
else
{
FileUpload1.SaveAs(Server.MapPath(#"~\ImgUpload\" + strFilePath));
Image1.ImageUrl = #"~\ImgUpload\" + strFilePath;
ViewState["fname"] = fname;
ViewState["fPath"] = #"~\ImgUpload\" + strFilePath;
XmlElement Ximages = XDoc.CreateElement("images");
Ximages.InnerText = fpath;
XCloths.AppendChild(Ximages);
}
}
else
{
}**
The problem is if i submitting each time the date is going to Reload the same id "1"
i want to continue to id 1,2,3,4,5................in one xml file
Unless you have some intricate purpose for your dataset, I believe you can drop it entirely.
Then you'd have something as follows:
XDocument xDoc;
int lastId = 0;
var path = Server.MapPath("data.xml");
if (File.Exists(path))
{
xDoc = XDocument.Load(path);
var existingCloths = xDoc.Root.Elements("cloths");
if (existingCloths.Any())
{
lastId = existingCloths.Max(c => Int32.Parse(c.Attribute("Id").Value));
}
}
else
{
xDoc = new XDocument(new XElement("products"));
}
var xCloths = XDoc.CreateElement("cloths");
xDoc.Add(new XAttribute("Id",
(lastId + 1).ToString(CultureInfo.InvariantCulture));
xDoc.Root.Add(xCloths);
//[...]
I personally find it significantly easier to just create a Class and serialize and deserialize to xml.
If you want to customize the Class you will need to add in the appropriate [XMLRoot] [XMLElement] and [XMLAttribute] tags to the Products class
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Xml.Serialization;
using System.Xml;
namespace UShort
{
class Program
{
static void Main(string[] args)
{
Products prod = new Products();
prod.Cloths = new List<Products.cloths>();
Products.cloths jacket = new Products.cloths();
jacket.ClothName = "MyJacket";
jacket.amount = 12345;
jacket.price = (float)123.45;
jacket.size = 12;
jacket.images = "c:\\asdasd.jpg";
prod.Cloths.Add(jacket);
// String contining XML. Do what you want with it.
string ProductXML = XMLTools.convertObjToXMLString(prod);
// Create an object from an XML string of the same format.
Products NewProduct = (Products)XMLTools.convertXMLStringToObject(ProductXML, typeof(Products));
}
public class Products
{
public List<cloths> Cloths = new List<cloths>();
public class cloths
{
public string ClothName = string.Empty;
public int size = 0;
public float price = 0;
public long amount = 0;
public string images = string.Empty;
}
}
public static class XMLTools
{
/// <summary>
/// Overwrites the encoding to use UTF-8
/// </summary>
private class Utf8StringWriter : StringWriter
{
public override Encoding Encoding
{
get { return Encoding.UTF8; }
}
}
public static string convertObjToXMLString(object obj)
{
try
{
XmlSerializer serObj = new XmlSerializer(obj.GetType());
Utf8StringWriter sw = new Utf8StringWriter();
XmlTextWriter xtw = new XmlTextWriter(sw);
xtw.Formatting = Formatting.Indented;
serObj.Serialize(xtw, obj);
return sw.ToString();
}
catch (Exception)
{
throw;
}
}
public static object convertXMLStringToObject(string xmlString, Type objectType)
{
try
{
TextReader txr = new StringReader(xmlString);
XmlSerializer serObj = new XmlSerializer(objectType);
return serObj.Deserialize(txr);
}
catch (Exception)
{
throw;
}
}
}
}
}

Pdf Merge Issue in ItextSharp (After Merging Pdfs don't retain their Values)

We are trying to merge three PDFs using ITextSharp. The problem is after merging we are able to get Data from the first PDF only, while the other two PDFs don't retain their values.
All these PDFs have the same structure (i.e. they use the same templates with different data), so my assumption is they are having same fields (AcroFields) which may be creating this problem while merging.
Here is the merge code :
public void MergeFiles(string destinationFile, string[] sourceFiles)
{
try
{
int f = 0;
string outFile = destinationFile;
Document document = null;
PdfCopy writer = null;
while (f < sourceFiles.Length)
{
// Create a reader for a certain document
PdfReader reader = new PdfReader(sourceFiles[f]);
// Retrieve the total number of pages
int n = reader.NumberOfPages;
//Trace.WriteLine("There are " + n + " pages in " + sourceFiles[f]);
if (f == 0)
{
// Step 1: Creation of a document-object
document = new Document(reader.GetPageSizeWithRotation(1));
// Step 2: Create a writer that listens to the document
writer = new PdfCopy(document, new FileStream(outFile, FileMode.Create));
// Step 3: Open the document
document.Open();
}
// Step 4: Add content
PdfImportedPage page;
for (int i = 0; i < n; )
{
++i;
if (writer != null)
{
page = writer.GetImportedPage(reader, i);
writer.AddPage(page);
}
}
PRAcroForm form = reader.AcroForm;
if (form != null)
{
if (writer != null)
{
writer.CopyAcroForm(reader);
}
}
f++;
}
// Step 5: Close the document
if (document != null)
{
document.Close();
}
}
catch (Exception)
{
//handle exception
}
}
This is called as follows :
string[] sourcenames = { #"D:\1.pdf", #"D:\2.pdf", #"D:\3.pdf" };
string destinationname = #"D:\pdf\mergeall\merge3.pdf";
MergeFiles(destinationname, sourcenames);
I figured it out myself after little searching...Following is the solution...
I have created the function to rename the Fields in the PDF,so that after merging the fields will be renamed.
private static int counter = 0;
private void renameFields(PdfReader pdfReader)
{
try
{
string prepend = String.Format("_{0}", counter++);
foreach (DictionaryEntry de in pdfReader.AcroFields.Fields)
{
pdfReader.AcroFields.RenameField(de.Key.ToString(), prepend + de.Key.ToString());
}
}
catch (Exception ex)
{
throw ex;
}
}
This function is called in "MergeFiles" function as follow...
// Create a reader for a certain document
PdfReader reader = new PdfReader(sourceFiles[f]);
renameFields(reader);
// Retrieve the total number of pages
int n = reader.NumberOfPages;

Categories