I have the following method (below), as you can see it serializes an object to an XML file. The main problem I am having is I want to get the function to overwrite a file if it exists. I know I could delete the file first if it does exist, but this would also mean that I might induce some error drag into my application. So I want it an all or nothing, overwrite method...
Here is the function, any ideas on how this can be accomplished?
/// <summary>
/// Serializes an object to an xml file.
/// </summary>
/// <param name="obj">
/// The object to serialize.
/// </param>
/// <param name="type"开发者_如何学运维>
/// The class type of the object being passed.
/// </param>
/// <param name="fileName">
/// The filename where the object should be saved to.
/// </param>
/// <param name="xsltPath">
/// Pass a null if not required.
/// </param>
public static void SerializeToXmlFile(object obj, Type type, string fileName, string xsltPath )
{
var ns = new XmlSerializerNamespaces();
ns.Add(String.Empty, String.Empty);
var serializer = new XmlSerializer(type);
var settings = new XmlWriterSettings {Indent = true, IndentChars = "\t"};
using (var w = XmlWriter.Create(fileName,settings))
{
if (!String.IsNullOrEmpty(xsltPath))
{
w.WriteProcessingInstruction("xml-stylesheet", "type=\"text/xsl\" href=\"" + xsltPath + "\"");
}
serializer.Serialize(w, obj, ns);
}
}
Use the overloaded version of the XmlWriter.Create
that takes a Stream
instead of a string, and use File.Create
to create/overwrite the file:
using (var w = XmlWriter.Create(File.Create(fileName), settings))
...
- Open the file using File.Open() with FileMode.Create, FileAccess.Write and FileShare.None.
- Pass the stream returned from File.Open() into XmlWriter.Create().
-
FileStream stream = File.Open(filename, FileMode.Create, FileAccess.Write, FileShare.None);
using (XmlWriter writer = XmlWriter.Create(stream))
{
...
}
The FileStream and XMLWriter should be placed in a using block
using (FileStream fs = File.Create(filename))
using (var w = XmlWriter.Create(fs, settings))
{
// your code
}
I believe that using the code below will fail to release the file stream. So if you run the code twice in one session it will fail
using (var w = XmlWriter.Create(File.Create(filename), settings))
Make a Backup of the Destination file if exist, if an error occurred, write back the file.
You can do something like following. Write your xml to a StringBuilder() and than write content of stringBuilder to file.
public static void SerializeToXmlFile(object obj, Type type, string fileName, string xsltPath) {
var ns = new XmlSerializerNamespaces();
ns.Add(String.Empty, String.Empty);
var serializer = new XmlSerializer(type);
var settings = new XmlWriterSettings { Indent = true, IndentChars = "\t" };
StringBuilder sb = new StringBuilder();
using (var w = XmlWriter.Create(sb, settings)) {
if (!String.IsNullOrEmpty(xsltPath)) {
w.WriteProcessingInstruction("xml-stylesheet", "type=\"text/xsl\" href=\"" + xsltPath + "\"");
}
serializer.Serialize(w, obj, ns);
}
File.WriteAllText(fileName, sb.ToString());
}
You could save your new XML file in a temporary file :
./directory/myNewObject.xml.temp
then rename myNewObject.xml.temp in myNewObject.xml using File.MoveTo
Pay attention to the FileMode, if you use FileMode.OpenOrCreate it will not remove the old file contents, if the new contents are smaller then the old contents the xml will be corrupt. Make sure to use FileMode.Create.
here is an extension, it writes to a local .tmp file first, if that succeeds it replaces the desired filename. I chose to use File.Copy to allow .NET.
If you are even more paranoid, you can find examples online of how to create an NTFS transaction for the file swap although it requires extern calls. e.g. https://improve.dk/utilizing-transactional-ntfs-through-dotnet/
depending on where it fails (stage) it will attempt to cleanup the .tmp file.
/// <summary>
/// asynchronous serializing of an object at the path specified by the FileInfo.
/// </summary>
/// <typeparam name="T">Type of the object to serialize</typeparam>
/// <param name="fi">FileInfo to the target path</param>
/// <param name="ObjectToSerialize">object to serialize</param>
/// <param name="fileShare">File sharing mode during write</param>
public static void SerializeXmlFile<T>(this FileInfo fi, T ObjectToSerialize, FileShare fileShare) //where T:IXmlSerializable
{
XmlSerializer serializer = new XmlSerializer(typeof(T));
var TargetFile = fi.FullName;
var fiTemp = new FileInfo(fi.FullName + ".tmp");
int Stage = 0;
try
{
try
{
using (StreamWriter writer = new StreamWriter(fiTemp.Open(FileMode.Create, FileAccess.Write, fileShare)))
{
serializer.Serialize(writer, ObjectToSerialize);
}
}
catch (Exception e)
{
throw new IOException("Unable to serialize to temp file, Error: " + e.Message, e);
}
Stage = 1;
try
{
fiTemp.CopyTo(TargetFile, true);
Stage = 2;
}
catch (Exception e)
{
throw new IOException("Unable to serialize to final file, Error replacing from temp: " + e.Message, e);
}
try
{
fiTemp.Delete();
} catch (FileNotFoundException) { }
catch (Exception e)
{
throw new IOException("Unable to cleanup temp file, Error: " + e.Message, e);
}
Stage = 3;
fi.Refresh();
}
catch (Exception)
{
throw;
}
finally
{
switch (Stage)
{
case 1: // temp is written
case 2: // temp is copied to destination, not yet deleted
{
try
{
fiTemp.Delete();
}
catch (FileNotFoundException) { }
}
break;
}
}
}
精彩评论