I have 2 XML files.
I want to compare them using XML Reader, for this I have already built a XML Reader method and provided the attributes for the Reader Settings.
Now the reader should run through, compare the two into a hash. If the hash is the same, then try catch to delete the old or the new one.
My problem is:
The reader needs an integer to compare the hashes (this is what I see)
However, I can only convert the XML path into a string and then pass it.
How do I get these files into the reader ?
private string xmlOld = #"c:\temp\List-FULL(xsd).xml".ToString();
private string xmlNew = #"c:\temp\List-FULL(xsd)2.xml".ToString();
Here is the Method:
public bool XmlListReader()
{
XmlReaderSettings settings = new XmlReaderSettings();
settings.IgnoreComments = true;
settings.IgnoreProcessingInstructions = true;
settings.IgnoreWhitespace = true;
try
{
int xmlHashNew = settings.GetHashCode(); // Part to Load 2 XML files, with Readersettings.
int xmlHashOld = settings.GetHashCode();
File.Delete(xmlHashAlt.Equals(xmlHashNeu) == false ? xmlOld : xmlNew);
return true;
}
catch (Exception e)
{
Console.WriteLine(e);
return false;
}
}
Thanks in advance
Related
I have a service that is processing data and exporting out xml files for another service, which usually handles small amount of data(About 5 files a minute) but sometimes a large chunk of data is dumped and it may handle needing to create 100 files all at once.
The service runs mostly fine but it occasionally runs into an error:
The process cannot access the file 'C:\File.xml' because it is being used by another process.
This is strange because the file should be created, written, and closed all in one shot, so I don't know what could be giving this problem. The issue seems to happen more frequently under heavy load, but still occurs regularly under light loads as well. Naturally this never shows up while debugging or I probably would have fixed this by now.
Here is the function that actually writes the file:
public bool WriteDocument(Object Data)
{
XmlDocument X = new XmlDocument();
XmlSchemaSet schemaSet = new XmlSchemaSet();
schemaSet.Add(null, "Schema.xsd");
X.Schemas = schemaSet;
if (!Directory.Exists("OutputDirectory"))
{
Directory.CreateDirectory("OutputDirectory");
}
string File = Path.Combine("OutputDirectory", #"File" + ".xml");
XmlSerializer Encoder = new XmlSerializer(typeof(Object));
//Do stuff to data object to prepare for output
using (MemoryStream buffer = new MemoryStream())
{
Encoder.Serialize(buffer, Data);
buffer.Position = 0;
X.Load(buffer);
X.Validate((sender, e) =>
{
if (e.Severity == XmlSeverityType.Error)
{
result = false;
}
else
{
result = true;
}
});
}
if (result)
{
while (System.IO.File.Exists(File))
{
File = MakeUniqueName(File);
}
XmlWriterSettings settings = new XmlWriterSettings();
settings.Encoding = Encoding.UTF8;
using (XmlWriter Writer = XmlWriter.Create(File, settings))
{
X.WriteTo(Writer);
Writer.Close();
}
}
return result;
}
I made a code where the I selected several XML files and save them on a DataSet array. So if I selected 5 XMl files, the DataSet array size is 5 (0-4), the tables in the dataset is filled with the XML nodes and subnodes
The first part of the code shows the way I saved the XML data into the DataSet array
Here is part of the code
public partial Form1:Form
{
XmlReader xmlFile;
DataSet[] XMlTable;
string[] FileNames;
string[] File;
.
.
.
private void Open_File_Btn_Click(object sender, EventArgs e)
{
OpenFileDialog openXmlFile = new OpenFileDialog();
//Multiselect allows openfiledialog to select multiple files
openXmlFile.Multiselect = true;
openXmlFile.Filter = "XML Files |*.xml";
openXmlFile.Title = "Select XML Files";
int size;
dataGridView1.Anchor = (AnchorStyles.Left | AnchorStyles.Bottom | AnchorStyles.Right | AnchorStyles.Top);
try
{
if (openXmlFile.ShowDialog() == DialogResult.OK)
{
if (!string.Equals(Path.GetExtension(openXmlFile.FileName), ".xml", StringComparison.OrdinalIgnoreCase))
{
//invalid file type selected; display an error
MessageBox.Show("The selected file is not supported by this application. You must select an XML file.", "Invalid File Type", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
else
{
//Find a way to show the selected file names only - right now it shows the whole address of the files
this.textBox1.Text = openXmlFile.FileName;
FileNames = openXmlFile.FileNames;
File = openXmlFile.SafeFileNames;
size = FileNames.Length;
for (int FileNumber = 0; FileNumber < FileNames.Length; FileNumber++)
{
//Deletes the node ICAD from XML file, if the ICAD node is present in an XML file and is not remove then the Datagridview will only show one column with the Icad Version
xmldoc.Load(FileNames[FileNumber]);
XmlNode nodeToDelete = xmldoc.SelectSingleNode("/conveyors/ICAD");
if (nodeToDelete != null)
{
nodeToDelete.ParentNode.RemoveChild(nodeToDelete);
xmldoc.Save(FileNames[FileNumber]);
} //node does not exists
XmlReaderSettings settings = new XmlReaderSettings();
settings.IgnoreWhitespace = false;
xmlFile = XmlReader.Create(FileNames[FileNumber], settings);
XMlTable = new DataSet[size];
XMlTable[FileNumber] = new DataSet();
XMlTable[FileNumber].ReadXml(xmlFile);
xmlFile.Close();
Build_Manifest.Enabled = true;
Show_Table.Enabled = true;
}
}
}
}
catch (Exception error)
{
MessageBox.Show(error.Message);
}
}
The code below is where the System.NullReferencesException error occurs and it only happens when I select more than one XML file
private void test1_Click(object sender, EventArgs e)
{
//Shows how big is the size of the Dataset array - it should be the same as the amount of selected files (5 files selected then array size = 5 (0-4)
MessageBox.Show(Convert.ToString(string.Format("Size of Array ={0}", XMlTable.Length)));
for (int test1 = 0; test1 < XMlTable.Length; test1++)
{
MessageBox.Show(Convert.ToString(string.Format("Tables used = {0}",XMlTable[test1].Tables.Count)));
MessageBox.Show(XMlTable[test1].Tables[0].TableName);
}
}
Your problem is that you re-initialize XMlTable every iteration.
Thus, only the last dataset in the array is actually present. The rest are nulls.
Move the XMlTable = new DataSet[size]; before the loop.
Here, i created a class, and what i am trying to accomplish is to write the contents from the list into an xml file.
1) At first run, it creates the file and trows an error here: Token EndElement in state EndRootElement would result in an invalid XML document
public static void SaveCellPhoneProducts(List<ProducCellPhone> LocalProducts)
{
XmlWriterSettings localSettings = new XmlWriterSettings();
localSettings.Indent = true;
localSettings.IndentChars = (" ");
//second run, error Occurr here
//xml writer object, CellPhoneProduct
XmlWriter xmlOut = XmlWriter.Create(path, localSettings);
xmlOut.WriteStartDocument();
xmlOut.WriteStartElement("Cell");
foreach(ProducCellPhone localProduct in LocalProducts)
{
WriteCellPhoneProductBase(localProduct, xmlOut);
}
//first Run error is thrown in here.
xmlOut.WriteEndElement();
xmlOut.Close();
}
2) When i rerun on the second time, the error is in same method.
public static void SaveCellPhoneProducts(List<ProducCellPhone> LocalProducts)
{
XmlWriterSettings localSettings = new XmlWriterSettings();
localSettings.Indent = true;
localSettings.IndentChars = (" ");
//xml writer object, CellPhoneProduct
XmlWriter xmlOut = XmlWriter.Create(path, localSettings);
xmlOut.WriteStartDocument();
xmlOut.WriteStartElement("Cell");
foreach(ProducCellPhone localProduct in LocalProducts)
{
WriteCellPhoneProductBase(localProduct, xmlOut);
}
xmlOut.WriteEndElement();
xmlOut.Close();
}
The whole class i here:
class ProductCellPhoneDB
{
private const string path = #"..\..\CellPhoneProducts.xml";
public static List<ProducCellPhone> GetCellPhoneProducts()
{
List<ProducCellPhone> localCellPhoneProducts =
new List<ProducCellPhone>();
XmlReaderSettings localSettings = new XmlReaderSettings();
localSettings.IgnoreWhitespace = true;
localSettings.IgnoreComments = true;
XmlReader xmlIn = (XmlReader.Create(path,localSettings));
if (xmlIn.ReadToDescendant("Cell"))
{
do
{
ProducCellPhone localProduct = null;
xmlIn.ReadStartElement("Cell");
localCellPhoneProducts.Add(localProduct);
}
while (xmlIn.ReadToNextSibling("Cell"));
}
xmlIn.Close();
return localCellPhoneProducts;
}
public static void SaveCellPhoneProducts(List<ProducCellPhone> LocalProducts)
{
XmlWriterSettings localSettings = new XmlWriterSettings();
localSettings.Indent = true;
localSettings.IndentChars = (" ");
//Error Occurr here
//xml writer object, CellPhoneProduct, error is being used by other process?
XmlWriter xmlOut = (XmlWriter.Create(path, localSettings));
xmlOut.WriteStartDocument();
xmlOut.WriteStartElement("Cell");
foreach(ProducCellPhone localProduct in LocalProducts)
{
WriteCellPhoneProductBase(localProduct, xmlOut);
}
//ERROR Token EndElement in state EndRootElement would result in an invalid XML document
xmlOut.WriteEndElement();
xmlOut.Close();
}
private static void ReadCellphoneProductBase(XmlReader xmlIn, ProducCellPhone localProduct)
{
localProduct.Iemi = xmlIn.ReadElementContentAsString();
localProduct.Model = xmlIn.ReadContentAsString();
localProduct.Price = xmlIn.ReadContentAsDecimal();
}
private static void WriteCellPhoneProductBase(ProducCellPhone localProduct,
XmlWriter xmlout)
{
xmlout.WriteElementString("IEMI", localProduct.Iemi);
xmlout.WriteElementString("Model", localProduct.Model);
xmlout.WriteElementString("Price", Convert.ToString(localProduct.Price));
xmlout.WriteEndElement();
}
}
Any suggestions would be helpful. Thanks community. !
The first error
you get is likely because the WriteStartElement and WriteEndElement calls are not matched. You do xmlOut.WriteStartElement("Cell"); once, but do xmlout.WriteEndElement(); several times, once for each ProducCellPhone in LocalProducts, plus another time after the foreach.
To solve this (if I guessed your XML document structure right), you should change your WriteCellPhoneProductBase method to:
private static void WriteCellPhoneProductBase(ProducCellPhone localProduct,
XmlWriter xmlout)
{
xmlOut.WriteStartElement("Cell");
xmlout.WriteElementString("IEMI", localProduct.Iemi);
xmlout.WriteElementString("Model", localProduct.Model);
xmlout.WriteElementString("Price", Convert.ToString(localProduct.Price));
xmlout.WriteEndElement();
}
And remove the WriteStartElement and WriteEndElement lines from SaveCellPhoneProducts (see below).
The second error is probably because the XmlWriter used when you got the first error was not disposed and has not closed the file handle. You should always use a using block to ensure IDisposable resources get disposed, also when an exception occurs. You should change your method to:
public static void SaveCellPhoneProducts(List<ProducCellPhone> LocalProducts)
{
//xml writer settings
XmlWriterSettings localSettings = new XmlWriterSettings();
localSettings.Indent = true;
localSettings.IndentChars = (" ");
using (var xmlOut = XmlWriter.Create(path, localSettings))
{
xmlOut.WriteStartDocument();
//write each product on xml file
foreach(ProducCellPhone localProduct in LocalProducts)
WriteCellPhoneProductBase(localProduct, xmlOut);
xmlOut.WriteEndDocument()
}
}
For your GetCellPhoneProducts follow the same using block approach.
I am currently working with an xml file which usually should contain a list of custom objects (List), but from time to time can simply contain a string node with a message. I have a code which deserializes this file:
private T DeserializeFile<T>(string filePath)
{
StreamReader readFileStream = new StreamReader(#filePath);
var serializerObj = new XmlSerializer(typeof(T));
return (T)serializerObj.Deserialize(readFileStream);
}
This works for List but for simple string throws an error (InvalidOperationException - Root element is missing, or " was not expected."). How can I detect the case when the file contains only the string element and return null from the function?
Basically this is what I want to do:
private T DeserializeFile<T>(string filePath)
{
StreamReader readFileStream = new StreamReader(#filePath);
var serializerObj = new XmlSerializer(typeof(T));
try
{
return (T)serializerObj.Deserialize(readFileStream);
}
catch (Exception ex)
{
return null;
}
}
This should do what you're trying to do. It uses XDocument to load and parse the file, so that it can check if there are any elements before trying to deserialize it.
private T DeserializeFile<T>(string filePath)
{
var xdoc = XDocument.Load(filePath);
if (xdoc.Root.Elements().Any())
{
var serializerObj = new XmlSerializer(typeof(T));
return (T)serializerObj.Deserialize(xdoc.CreateReader());
}
else
return default(T);
}
This assumes that you always have valid XML files, with the only difference being whether the root contains more elements or just text, e.g. a list like
<someRoot>
<someObj>
</someObj>
<someObj>
</someObj>
</someRoot>
And a "string" like
<someRoot>just a string</someRoot>
I am looking on Internet how keep the carriage return from XML data but I could not find the answer, so I'm here :)
The objective is to write in a file the content of a XML data. So, if the value of the node contains some "\r\n" data, the soft need to write them in file in order to create new line, but it doesn't write, even with space:preserve.
Here is my test class:
XElement xRootNode = new XElement("DOCS");
XElement xData = null;
//XNamespace ns = XNamespace.Xml;
//XAttribute spacePreserve = new XAttribute(ns+"space", "preserve");
//xRootNode.Add(spacePreserve);
xData = new XElement("DOC");
xData.Add(new XAttribute("FILENAME", "c:\\titi\\prout.txt"));
xData.Add(new XAttribute("MODE", "APPEND"));
xData.Add("Hi my name is Baptiste\r\nI'm a lazy boy");
xRootNode.Add(xData);
bool result = Tools.writeToFile(xRootNode.ToString());
And here is my process class:
try
{
XElement xRootNode = XElement.Parse(xmlInputFiles);
String filename = xRootNode.Element(xNodeDoc).Attribute(xAttributeFilename).Value.ToString();
Boolean mode = false;
try
{
mode = xRootNode.Element(xNodeDoc).Attribute(xWriteMode).Value.ToString().ToUpper().Equals(xWriteModeAppend);
}
catch (Exception e)
{
mode = false;
}
String value = xRootNode.Element(xNodeDoc).Value;
StreamWriter destFile = new StreamWriter(filename, mode, System.Text.Encoding.Unicode);
destFile.Write(value);
destFile.Close();
return true;
}
catch (Exception e)
{
return false;
}
Does anybody have an idea?
If you want to preserve cr lf in element or attribute content when saving a XDocument or XElement you can do that by using certain XmlWriterSettings, namely NewLineHandling to Entitize:
string fileName = "XmlOuputTest1.xml";
string attValue = "Line1.\r\nLine2.";
string elementValue = "Line1.\r\nLine2.\r\nLine3.";
XmlWriterSettings xws = new XmlWriterSettings();
xws.NewLineHandling = NewLineHandling.Entitize;
XDocument doc = new XDocument(new XElement("root",
new XAttribute("test", attValue),
elementValue));
using (XmlWriter xw = XmlWriter.Create(fileName, xws))
{
doc.Save(xw);
}
doc = XDocument.Load(fileName);
Console.WriteLine("att value: {0}; element value: {1}.",
attValue == doc.Root.Attribute("test").Value,
elementValue == doc.Root.Value);
In that example the value are preserved in the round trip of saving and loading as the output of the sample is "att value: True; element value: True."
Heres a useful link I found for parsing an Xml string with carraige returns, line feeds in it.
howto-correctly-parse-using-xelementparse-for-strings-that-contain-newline-character-in
It may help those who are parsing an Xml string.
For those who can't be bothered to click it says use an XmlTextReader instead
XmlTextReader xtr = new XmlTextReader(new StringReader(xml));
XElement items = XElement.Load(xtr);
foreach (string desc in items.Elements("Item").Select(i => (string)i.Attribute("Description")))
{
Console.WriteLine("|{0}|", desc);
}