I want something like
try
{
//code here
}
catch (Exception ex)
{
stringXML = Exception.toXML();
}
so that the value of stringXML would be
<exception><message></message><innerException></innerException></exception>
For example...
How is this possible?
It depends how much code you want to write. One simple approach would be to write your own object and use XmlSerializer:
[XmlRoot("exception"), XmLType("exception")]
public class SerializableException {
[XmlElement("message")]
public string Message {get;set;}
[XmlElement("innerException")]
public SerializableException InnerException {get;set;}
}
and just map a regular exception into this. But since it is simple anyway, maybe XmlWriter is good enough...
public static string GetXmlString(this Exception exception)
{
if (exception == null) throw new ArgumentNullException("exception");
StringWriter sw = new StringWriter();
using (XmlWriter xw = XmlWriter.Create(sw))
{
WriteException(xw, "exception", exception);
}
return sw.ToString();
}
static void WriteException(XmlWriter writer, string name, Exception exception)
{
if (exception == null) return;
writer.WriteStartElement(name);
writer.WriteElementString("message", exception.Message);
writer.WriteElementString("source", exception.Source);
WriteException(writer, "innerException", exception.InnerException);
writer.WriteEndElement();
}
Someone has already wrote a blog entry about it
using System;
using System.Collections;
using System.Linq;
using System.Xml.Linq;
/// <summary>Represent an Exception as XML data.</summary>
public class ExceptionXElement : XElement
{
/// <summary>Create an instance of ExceptionXElement.</summary>
/// <param name="exception">The Exception to serialize.</param>
public ExceptionXElement(Exception exception)
: this(exception, false)
{ }
/// <summary>Create an instance of ExceptionXElement.</summary>
/// <param name="exception">The Exception to serialize.</param>
/// <param name="omitStackTrace">
/// Whether or not to serialize the Exception.StackTrace member
/// if it's not null.
/// </param>
public ExceptionXElement(Exception exception, bool omitStackTrace)
: base(new Func<XElement>(() =>
{
// Validate arguments
if (exception == null)
{
throw new ArgumentNullException("exception");
}
// The root element is the Exception's type
XElement root = new XElement
(exception.GetType().ToString());
if (exception.Message != null)
{
root.Add(new XElement("Message", exception.Message));
}
// StackTrace can be null, e.g.:
// new ExceptionAsXml(new Exception())
if (!omitStackTrace && exception.StackTrace != null)
{
root.Add
(
new XElement("StackTrace",
from frame in exception.StackTrace.Split('\n')
let prettierFrame = frame.Substring(6).Trim()
select new XElement("Frame", prettierFrame))
);
}
// Data is never null; it's empty if there is no data
if (exception.Data.Count > 0)
{
root.Add
(
new XElement("Data",
from entry in
exception.Data.Cast<DictionaryEntry>()
let key = entry.Key.ToString()
let value = (entry.Value == null) ?
"null" : entry.Value.ToString()
select new XElement(key, value))
);
}
// Add the InnerException if it exists
if (exception.InnerException != null)
{
root.Add
(
new ExceptionXElement
(exception.InnerException, omitStackTrace)
);
}
return root;
})())
{ }
}
Related
I wrote code to delete a folder with all its structure recursively.
I got System.IO.DirectoryNotFoundException on File.Delete for a valid filename that I got from Directory.GetFiles(path). I added File.Exists before and it tells me that the file does not exists either of I can see it in Explorer.
I know I can do it with SHFileOperation which should work fine. But I would like to use native C# (no direct Windows API).
I red: .NET 4.6.2 and long paths on Windows 10 and set enable "Enable Win 32 long paths" in local group policy editor.
I'm using .NET Framework 4.7.2.
Does anyone can tell me what's wrong in my code? Or what I can do to make it works?
The path in error is:
'E:\CobianBackupOld\cn1629\AppData\Local\Packages\Microsoft.Windows.Cortana_cw5n1h2txyewy\LocalState\AppIconCache\100\{7C5A40EF-A0FB-4BFC-874A-C0F2E0B9FA8E}_Microsoft Visual Studio 9_0_Application_PreEmptive Solutions_Dotfuscator Community Edition_dotfuscator_exe'
I'm the owner of that file (I do have privilege to delete it).
UPDATE
I partly fixed my error but would like to know how to fix it properly (Recent frameworks are suppose to support long path???).
My actual fix is to add that code at start of my recursive function
if (path.Length < 2 || !path.StartsWith(#"\\"))
{
path = #"\\?\" + path;
}
UPDATE 2 Although not directly related... Just want to bring an important point, C# File.Delete or Directory.Delete does NOT delete
readonly file or folder, you should also change their permissions to
normal before deleting them, otherwise you get an access denied error.
FYI, my code is:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace HQ.Util.General.IO
{
public class DirectoryRemoverRecursive
{
private readonly Action<string, string> _pathStatus;
private readonly Func<string, bool> _callbackCanRemoveFile;
private readonly Func<string, bool> _callbackCanRemoveFolder;
private Func<bool> _shouldCancel;
/// <summary>
///
/// </summary>
/// <param name="pathStatus">Arguments are [path] and [null on success or exception message]</param>
/// <param name="callbackCanRemoveFile">Argument is path and should return true to delete. If this function is null, all path will be deleted.</param>
/// <param name="callbackCanRemoveFolder">Argument is path and should return true to delete. If this function is null, all path will be deleted.</param>
/// <param name="shouldCancel">If null will never cancel. Cancel when func return true</param>
public DirectoryRemoverRecursive(
Action<string, string> pathStatus = null,
Func<string, bool> callbackCanRemoveFile = null,
Func<string, bool> callbackCanRemoveFolder = null,
Func<bool> shouldCancel = null)
{
_pathStatus = pathStatus;
_callbackCanRemoveFile = callbackCanRemoveFile;
_callbackCanRemoveFolder = callbackCanRemoveFolder;
_shouldCancel = shouldCancel;
}
// ******************************************************************
/// <summary>
/// return true if canceled
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
public bool Remove(string path)
{
string result = null;
if (Directory.Exists(path))
{
foreach (var subDir in Directory.GetDirectories(path))
{
if (_shouldCancel != null)
{
if (_shouldCancel())
{
return true;
}
}
if (Remove(subDir))
{
return true;
}
}
foreach (var filename in Directory.GetFiles(path))
{
if (_shouldCancel != null)
{
if (_shouldCancel())
{
return true;
}
}
if (Remove(filename))
{
return true;
}
}
try
{
if (_callbackCanRemoveFolder != null)
{
if (!_callbackCanRemoveFolder(path))
{
return false;
}
}
Directory.Delete(path);
result = null;
}
catch (Exception ex)
{
result = ex.Message;
}
}
else
{
try
{
if (File.Exists(path))
{
if (_callbackCanRemoveFile != null)
{
if (!_callbackCanRemoveFile(path))
{
return false;
}
}
File.Delete(path);
result = null;
}
else
{
Debug.Print($"File does not exists {path}");
}
}
catch (Exception ex)
{
result = ex.Message;
}
}
_pathStatus?.Invoke(path, result);
return false;
}
// ******************************************************************
}
}
Although no as perfect as I would have wished, I fixed my bugs by using prefix "\?\" and by ensuring that attributes are set to normal prior to delete.
I still don't undertsand why I still have to do that??? Why it is still not fixed in recent .net library?
Final code:
using System;
using System.Diagnostics;
using System.IO;
namespace HQ.Util.General.IO
{
public class DirectoryRemoverRecursive
{
private readonly Action<string, string, int> _pathStatus;
private readonly Func<string, bool> _callbackCanRemoveFile;
private readonly Func<string, bool> _callbackCanRemoveFolder;
private Func<bool> _shouldCancel;
public int Count { get; private set; } = 0;
/// <summary>
///
/// </summary>
/// <param name="pathStatus">Arguments are [path] and [null on success or exception message]</param>
/// <param name="callbackCanRemoveFile">Argument is path and should return true to delete. If this function is null, all path will be deleted.</param>
/// <param name="callbackCanRemoveFolder">Argument is path and should return true to delete. If this function is null, all path will be deleted.</param>
/// <param name="shouldCancel">If null will never cancel. Cancel when func return true</param>
public DirectoryRemoverRecursive(
Action<string, string, int> pathStatus = null,
Func<string, bool> callbackCanRemoveFile = null,
Func<string, bool> callbackCanRemoveFolder = null,
Func<bool> shouldCancel = null)
{
_pathStatus = pathStatus;
_callbackCanRemoveFile = callbackCanRemoveFile;
_callbackCanRemoveFolder = callbackCanRemoveFolder;
_shouldCancel = shouldCancel;
}
// ******************************************************************
/// <summary>
/// return true if canceled
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
public bool Remove(string path)
{
string result = null;
if (path.Length < 2 || !path.StartsWith(#"\\"))
{
path = #"\\?\" + path;
}
if (Directory.Exists(path))
{
foreach (var subDir in Directory.GetDirectories(path))
{
if (_shouldCancel != null)
{
if (_shouldCancel())
{
return true;
}
}
if (Remove(subDir))
{
return true;
}
}
foreach (var filename in Directory.GetFiles(path))
{
if (_shouldCancel != null)
{
if (_shouldCancel())
{
return true;
}
}
if (Remove(filename))
{
return true;
}
}
try
{
if (_callbackCanRemoveFolder != null)
{
if (!_callbackCanRemoveFolder(path))
{
return false;
}
}
Directory.Delete(path);
Count++;
result = null;
}
catch (Exception ex)
{
try
{
File.SetAttributes(path, FileAttributes.Normal);
Directory.Delete(path);
Count++;
result = null;
}
catch (Exception)
{
result = "Try to delete directory exception: " + ex.ToString();
}
}
}
else
{
try
{
if (File.Exists(path))
{
if (_callbackCanRemoveFile != null)
{
if (!_callbackCanRemoveFile(path))
{
return false;
}
}
File.Delete(path);
Count++;
result = null;
}
else
{
Debug.Print($"File does not exists {path}");
}
}
catch (Exception ex)
{
try
{
File.SetAttributes(path, FileAttributes.Normal);
File.Delete(path);
Count++;
result = null;
}
catch (Exception)
{
result = "Try to delete file exception: " + ex.ToString();
}
}
}
_pathStatus?.Invoke(path, result, Count);
return false;
}
// ******************************************************************
}
}
When I DeSerialize an object from file, the fields whit equal references, don't have same references any more.
This is an example:
in this example, I created an object a1 from type A. then I saved it in a file and load it into new object named a2. In a1 there is b1 and b2 which are same (equal references), so when I set a1.b1.x = 5;, the value of a1.b2.x will change to 5 also, but after save/load, when I set a2.b1.x = 5;, the value of a2.b2.x will not change!!!
using System;
using System.Xml;
using System.Xml.Serialization;
using System.IO;
using System.Windows.Forms;
namespace test
{
public class SerializeObjectTest
{
public static void Test()
{
var a1 = new A();
a1.init();
SerializeObject<A>(a1, "d:\\1.xml");
var a2 = DeSerializeObject<A>("d:\\1.xml");
a1.b1.x = 5; // this will change also the value of a1.b2.x
a2.b1.x = 5; // this will not!!!!! change also the value of a2.b2.x
MessageBox.Show(
"a1.b1.x==a1.b2.x : " + a1.b1.x + "?=" + a1.b2.x + "\r\n" +
"a2.b1.1==a2.b2.x : " + a2.b1.x + "?=" + a2.b2.x + " !!\r\n", "Save.SaveAble"
);
}
public class A
{
public void init()
{
b1 = new B() { x = 100 };
b2 = b1;
}
public B b1;
public B b2;
}
public class B
{
public double x;
}
/// <summary>
/// Serializes an object.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="serializableObject"></param>
/// <param name="fileName"></param>
public static void SerializeObject<T>(T serializableObject, string fileName)
{
if (serializableObject == null) { return; }
try
{
XmlDocument xmlDocument = new XmlDocument();
XmlSerializer serializer = new XmlSerializer(serializableObject.GetType());
using (MemoryStream stream = new MemoryStream())
{
serializer.Serialize(stream, serializableObject);
stream.Position = 0;
xmlDocument.Load(stream);
xmlDocument.Save(fileName);
stream.Close();
}
}
catch (Exception ex)
{
//Log exception here
}
}
/// <summary>
/// Deserializes an xml file into an object list
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="fileName"></param>
/// <returns></returns>
public static T DeSerializeObject<T>(string fileName)
{
if (string.IsNullOrEmpty(fileName)) { return default(T); }
T objectOut = default(T);
try
{
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.Load(fileName);
string xmlString = xmlDocument.OuterXml;
using (StringReader read = new StringReader(xmlString))
{
Type outType = typeof(T);
XmlSerializer serializer = new XmlSerializer(outType);
using (XmlReader reader = new XmlTextReader(read))
{
objectOut = (T)serializer.Deserialize(reader);
reader.Close();
}
read.Close();
}
}
catch (Exception ex)
{
//Log exception here
}
return objectOut;
}
}
}
I developed an project my self that can save/load/clone objects in c# and it keeps references to object, it is available here.
It also can save internal and private fields. There are some attributes to how save fields or types (like donsave, saveas, saveif, ...).
I am working in xamarin.forms. I want to create background service for Android.
My requirement is to track device location (Latitude, longitude, place name, Employee code) and inserted to the web server every after 5 min. After successful login, service has been started and for a particular employee, data is start inserting. Even if the app is closed still service should be running in background and data is keep inserting.
My code is
using Android.App;
using Android.Content;
using Android.Locations;
using Android.Net;
using Android.OS;
using HRMS;
using HRMS.Interface;
using HRMS.TableAttributes;
using Newtonsoft.Json;
using Plugin.Geolocator;
using Plugin.Geolocator.Abstractions;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Threading;
using System.Xml.Linq;
using Xamarin.Forms;
using static HRMS.ServiceLayer.ServiceClasses;
using HRMS.MenuController;
using HRMS.Droid;
using Android.Provider;
namespace SilverHRMS.Droid.Service
{
[Service]
public class GPSTrackingService : Android.App.Service
{
#region Private Variables
static readonly string TAG = "X:" + typeof(GPSTrackingService).Name;
static int TimerWait = 300000; //150000;//25000 25 Sec; // 10 min 600000 and 20 Min 1200000
Timer _timer;
readonly string logTag = "GPSTrackingService";
private string deviceId = "";
private string googleAPI = "http://maps.googleapis.com/maps/api/geocode/xml?latlng={0},{1}&sensor=false";
#endregion
/// <summary>
///
/// </summary>
/// <param name="intent"></param>
/// <param name="flags"></param>
/// <param name="startId"></param>
/// <returns></returns>
public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
{
try
{
// Log.Debug(TAG, "OnStartCommand called at {2}, flags={0}, startid={1}", flags, startId, DateTime.UtcNow);
//_timer = new Timer(o => { Log.Debug(TAG, "Hello from GPSTrackingService. {0}", DateTime.UtcNow); GetCurrentLocation(); }, null, 0, TimerWait);
_timer = new Timer(o => { GetCurrentLocation(); }, null, 0, TimerWait);
//_timer = new Timer(o => { GetCurrentLocation(); }, null, 0, TimerWait);
}
catch (Exception ex)
{
var fileService = DependencyService.Get<ISaveException>();
fileService.SaveTextAsync(App.FileName, ex.StackTrace);
}
return StartCommandResult.Sticky;
}
/// <summary>
///
/// </summary>
/// <param name="intent"></param>
/// <returns></returns>
public override IBinder OnBind(Intent intent)
{
// This example isn't of a bound service, so we just return NULL.
return null;
}
/// <summary>
/// On Destroy App
/// </summary>
public override void OnDestroy()
{
base.OnDestroy();
_timer.Dispose();
_timer = null;
//Log.Debug(logTag, "Service has been terminated");
}
/// <summary>
/// Get Current Location and Insert to DB
/// </summary>
public async void GetCurrentLocation()
{
try
{
string statusMessage = string.Empty;
var locator = CrossGeolocator.Current;
locator.DesiredAccuracy = 100;
//Log.Debug(TAG, " getting GPS connection...");
if (App.MyEmployeeId != null)
{
#region Check Internet Connection
ConnectivityManager connectivityManager = (ConnectivityManager)GetSystemService(ConnectivityService);
NetworkInfo activeConnection = connectivityManager.ActiveNetworkInfo;
bool isOnline = (activeConnection != null) && activeConnection.IsConnected;
#endregion
#region Check GPS Location Position and Emp Code
//LocationManager locationManager = (LocationManager)GetSystemService(LocationService);
bool isGPSOn = App.CheckGPSConnection();
Position position = null;
if (isGPSOn)
{
try
{
position = await locator.GetPositionAsync(timeoutMilliseconds: 10000);
}
catch (Exception)
{
position = null;
}
}
#endregion
#region Insert Location to Local DB or Server DB
//Device Has Internet Connection
if (isOnline)
{
#region Online Location Insert Direct to the Server Database
if (position != null)
{
List<GPSTrackingLocationClass> lcListOnline = new List<GPSTrackingLocationClass>();
GPSTrackingLocationClass objGPSTrack = new GPSTrackingLocationClass();
objGPSTrack.Latitude = position.Latitude;
objGPSTrack.Longitude = position.Longitude;
objGPSTrack.EmpCd = App.MyEmployeeId;
objGPSTrack.GPS_Track_DateTime = App.GetDateTime(DateTime.Now);
IDevice device = DependencyService.Get<IDevice>();
deviceId = device.GetIdentifier();
deviceId = deviceId.Replace("+", "%2B");
objGPSTrack.DeviceId = deviceId;
string url = string.Format(googleAPI, position.Latitude, position.Longitude);
try
{
XElement xml = XElement.Load(url);
if (xml.Element("status").Value == "OK")
{
objGPSTrack.PlaceName = xml.Element("result").Element("formatted_address").Value;
}
else
{
objGPSTrack.PlaceName = string.Empty;
}
}
catch (Exception ex)
{
}
lcListOnline.Add(objGPSTrack);
if (lcListOnline.Count > 0)
{
string jsonContentsOnline = JsonConvert.SerializeObject(lcListOnline);
var responseOnline = await HRMS.ServiceLayer.GetResponseFromWebService.GetResponsePostData<HRMS.ServiceLayer.ServiceClasses.RootObject>(HRMS.ServiceLayer.ServiceURL.PostGPSLocation, jsonContentsOnline);
if (responseOnline.Flag == true)
{
statusMessage = responseOnline.Message;
}
}
}
#endregion
#region Local Database Entries Insert to Server Database
List<LocationTracking> lcListOffline = SideMenu.repoLocation.GetAllLocationAsync();
if (lcListOffline != null)
{
if (lcListOffline.Count > 0)
{
ObservableCollection<LocationTracking> lvCollection = new ObservableCollection<LocationTracking>(lcListOffline);
List<GPSTrackingLocationClass> lcListGoingtoOnline = new List<GPSTrackingLocationClass>();
foreach (var item in lvCollection)
{
GPSTrackingLocationClass objGPSTrackOffline = new GPSTrackingLocationClass();
objGPSTrackOffline.Latitude = item.Latitude;
objGPSTrackOffline.Longitude = item.Longitude;
objGPSTrackOffline.EmpCd = item.EmpCd;
objGPSTrackOffline.GPS_Track_DateTime = item.GPS_Track_DateTime;
objGPSTrackOffline.DeviceId = item.DeviceID;
string urlAPI = string.Format(googleAPI, item.Latitude, item.Longitude);
try
{
XElement xmlURL = XElement.Load(urlAPI);
if (xmlURL.Element("status").Value == "OK")
{
objGPSTrackOffline.PlaceName = xmlURL.Element("result").Element("formatted_address").Value;
}
else
{
objGPSTrackOffline.PlaceName = string.Empty;
}
}
catch (Exception exe)
{
}
if (!string.IsNullOrEmpty(objGPSTrackOffline.PlaceName))
{
lcListGoingtoOnline.Add(objGPSTrackOffline);
}
}
if (lcListGoingtoOnline.Count == lvCollection.Count)
{
string jsonContentsOffline = JsonConvert.SerializeObject(lcListGoingtoOnline);
var responseOffline = await HRMS.ServiceLayer.GetResponseFromWebService.GetResponsePostData<HRMS.ServiceLayer.ServiceClasses.RootObject>(HRMS.ServiceLayer.ServiceURL.PostGPSLocation, jsonContentsOffline);
if (responseOffline.Flag == true)
{
statusMessage = responseOffline.Message;
SideMenu.repoLocation.RemoveLocationAsync();
}
}
}
}
#endregion
}
//Device Has No Internet Connection
else
{
try
{
if (position != null)
{
IDevice device = DependencyService.Get<IDevice>();
deviceId = device.GetIdentifier();
deviceId = deviceId.Replace("+", "%2B");
string newDate = App.GetDateTime(DateTime.Now);
SideMenu.repoLocation.AddNewLocationAsync(newDate, position.Latitude, position.Longitude, App.MyEmployeeId, deviceId, "--");
}
}
catch (Exception ex)
{
//Log.Debug(TAG, "Please turn on GPS in your headset");
}
}
#endregion
}
}
catch (Exception ex)
{
var fileService = DependencyService.Get<ISaveException>();
await fileService.SaveTextAsync(App.FileName, ex.StackTrace);
}
}
/// <summary>
/// Start Location Service
/// </summary>
public static void StartLocationService()
{
// Starting a service like this is blocking, so we want to do it on a background thread
//new Task(() =>
//{
// Start our main service
try
{
Intent intent = new Intent(Android.App.Application.Context, typeof(GPSTrackingService));
Android.App.Application.Context.StartService(intent);
}
catch (Exception ex)
{
var fileService = DependencyService.Get<ISaveException>();
fileService.SaveTextAsync(App.FileName, ex.StackTrace);
}
//}).Start();
}
/// <summary>
/// Start Location Service
/// </summary>
public static void StartApplication()
{
try
{
Intent start = new Intent(Android.App.Application.Context, typeof(MainActivity));
// my activity name is MainActivity replace it with yours
start.AddFlags(ActivityFlags.NewTask);
Android.App.Application.Context.ApplicationContext.StartActivity(start);
}
catch (Exception ex)
{
var fileService = DependencyService.Get<ISaveException>();
fileService.SaveTextAsync(App.FileName, ex.StackTrace);
}
}
public static void SendEmail(string text)
{
var email = new Intent(Android.Content.Intent.ActionSend);
email.PutExtra(Android.Content.Intent.ExtraEmail, new string[] { "pankit.patel#silvertouch.com" });
email.PutExtra(Android.Content.Intent.ExtraSubject, "Send Crash Report");
email.PutExtra(Android.Content.Intent.ExtraText, "Hello, " + text);
email.AddFlags(ActivityFlags.NewTask);
email.SetType("message/rfc822");
Android.App.Application.Context.ApplicationContext.StartActivity(email);
}
/// <summary>
/// Stop Location Service
/// </summary>
public static void StopLocationService()
{
// Check for nulls in case StartLocationService task has not yet completed.
//Log.Debug("App", "StopGPSTrackingService");
try
{
Intent intent = new Intent(Android.App.Application.Context, typeof(GPSTrackingService));
Android.App.Application.Context.StopService(intent);
}
catch (Exception ex)
{
var fileService = DependencyService.Get<ISaveException>();
fileService.SaveTextAsync(App.FileName, ex.StackTrace);
}
}
}
}
This service is only running when app is open (even in background) but if I close the app then service is automatically stop working. How to solve this issue? I have so many xamarin blogs regarding this topic but I din't get any workaround yet.
How to make background service that should be running even after app is close?
I am trying to deserialize an XML file into an object in the Program file of my Windows Forms application as below:
List<UserAccessGroup> AccessGroups = new List<UserAccessGroup>();
AccessGroups = SerializerHelper.DeSerializeObject<List<UserAccessGroup>>(#"C:\Users\Michael"
+ #"\Google Drive\FDM Dev Course Content\Workspace\SystemAdmin\SystemAdmin\"
+ #"XML Data Store\UserAccessGroups.xml");
UserAccessGroup SystemAdmin_App = new UserAccessGroup();
foreach (UserAccessGroup group in AccessGroups)
{
if (group.Name.Equals("Admin Operators"))
{
SystemAdmin_App = group;
}
}
When I run this code, I am getting an unhandled exception in my foreach loop, stating that Access Groups is null.
However, when I copy and paste this snippet of code into a blank console application, it runs fine and when I check AccessGroups with a break point, it has 4 members, as expected.
Can anyone please tell me why deserialization is not working in my program file?
Also, here is my XML file:
<?xml version="1.0"?>
<ArrayOfUserAccessGroup xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<UserAccessGroup>
<Name>Admin Operators</Name>
<Access_Group>
<int>999</int>
</Access_Group>
</UserAccessGroup>
<UserAccessGroup>
<Name>Shareholders</Name>
<Access_Group />
</UserAccessGroup>
<UserAccessGroup>
<Name>Brokers</Name>
<Access_Group />
</UserAccessGroup>
<UserAccessGroup>
<Name>StockExMgrs</Name>
<Access_Group />
</UserAccessGroup>
</ArrayOfUserAccessGroup>
EDIT: forgot to include the SerializerHelper class that I am using for serialization/deserialization, please see below:
public static class SerializerHelper
{
/// <summary>
/// Serializes an object.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="serializableObject"></param>
/// <param name="fileName"></param>
private static readonly log4net.ILog logger = log4net.LogManager.GetLogger(
"SerializerHelper.cs");
public static void SerializeObject<T>(string filepath, T serializableObject)
{
if (serializableObject == null) { return; }
try
{
XmlDocument xmlDocument = new XmlDocument();
XmlSerializer serializer = new XmlSerializer(serializableObject.GetType());
using (MemoryStream stream = new MemoryStream())
{
serializer.Serialize(stream, serializableObject);
stream.Position = 0;
xmlDocument.Load(stream);
xmlDocument.Save(filepath);
stream.Close();
}
}
catch (Exception ex)
{
//Log exception here
logger.Error("Error Serializing: " + ex.Message);
}
}
/// <summary>
/// Deserializes an xml file into an object list
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="fileName"></param>
/// <returns></returns>
public static T DeSerializeObject<T>(string filepath)
{
T objectOut = default(T);
if (!System.IO.File.Exists(filepath)) return objectOut;
try
{
string attributeXml = string.Empty;
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.Load(filepath);
string xmlString = xmlDocument.OuterXml;
using (StringReader read = new StringReader(xmlString))
{
Type outType = typeof(T);
XmlSerializer serializer = new XmlSerializer(outType);
using (XmlReader reader = new XmlTextReader(read))
{
objectOut = (T)serializer.Deserialize(reader);
reader.Close();
}
read.Close();
}
}
catch (Exception ex)
{
//Log exception here
logger.Error("Error Deserializing: " + ex.Message);
}
return objectOut;
}
}
EDIT: UserAccessGroup class below:
[Serializable]
public class UserAccessGroup : IUserAccessGroup
{
private String name;
private List<int> AccessGroup = new List<int>();
public String Name
{
get { return name; }
set { name = value; }
}
public List<int> Access_Group
{
get { return AccessGroup; }
set { AccessGroup = value; }
}
public UserAccessGroup()
{
}
public UserAccessGroup(String name)
{
this.name = name;
}
public List<int> getUserIDs()
{
return AccessGroup;
}
public void removeUser(int userID)
{
AccessGroup.Remove(userID);
return;
}
public void addUser(int userID)
{
AccessGroup.Add(userID);
return;
}
}
The main problem can be summarized as:
T objectOut = default(T);
if (!System.IO.File.Exists(filepath)) return objectOut;
try
{
// ...
}
catch (Exception ex)
{
//Log exception here
logger.Error("Error Deserializing: " + ex.Message);
}
return objectOut;
(note that default(T) for T=List<UserAccessGroup> is null)
So: for AccessGroups to be null, one of 2 things is happening:
the file does not exist (so the code is exiting near the top)
an exception is being thrown
Check each of these. If the first: add it. If the second: read the .Message, and the .InnerException.Message etc (XmlSerializer is very big on inner-exceptions)
XmlSerializer will not return null for the root object of a list / array, so: it is one of those two things.
Put a breakpoint on the not-exists return, and in the catch, and you should find what is happening. Alternatively, look at where-ever logger writes. Maybe also add something that writes to logger when the file doesn't exist.
I get an object of a class with some properties by calling its own static function for an instance. If there is a XML file the object tries to load it and add its values to the instance itself. Then it will save the XML again in case there are missing options in the XML file.
I created a small console app:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.IO;
using System.Reflection;
using System.Xml.Serialization;
using System.Xml;
namespace Test
{
public class Program
{
static void Main(string[] args)
{
TaskServerSettings s = TaskServerSettings.LoadNew();
}
}
public class TaskServerSettings : IEqualityComparer
{
#region SETTINGS PROPERTIES
public bool Enabled { get; set; }
public int CheckInterval { get; set; }
#endregion
#region CONSTRUCTORS AND METHODS
public TaskServerSettings()
{
this.init();
}
public TaskServerSettings(string settingsFile)
{
this.init();
if (settingsFile != null)
{
if (File.Exists(settingsFile))
{
this.Load(settingsFile);
}
this.Save(settingsFile);
}
}
private void init()
{
this.Enabled = true;
this.CheckInterval = 5000;
}
public void Absorb(TaskServerSettings newSettings)
{
this.Enabled = newSettings.Enabled;
this.CheckInterval = newSettings.CheckInterval;
}
public static TaskServerSettings LoadNew(string settingsFile = null)
{
if (settingsFile == null)
{
settingsFile = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location.TrimEnd('\\')) + #"\TaskServerSettings.xml";
}
return new TaskServerSettings(settingsFile);
}
public bool Load(string settingsFile = null)
{
if (settingsFile == null)
{
settingsFile = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location.TrimEnd('\\')) + #"\TaskServerSettings.xml";
}
if (!File.Exists(settingsFile))
{
throw new FileNotFoundException("Could not find \"" + settingsFile + "\" to load settings.");
}
bool result = false;
using (FileStream fs = new FileStream(settingsFile, FileMode.Open))
{
XmlSerializer xs = new XmlSerializer(this.GetType());
if (!xs.CanDeserialize(XmlReader.Create(fs)))
{
throw new XmlException("\"" + settingsFile + "\" does not have a valid TaskServerSettings XML structure.");
}
//try
//{ // +- InvalidOperationException - Error in XML document (0,0).
// v The root element is missing.
this.Absorb(xs.Deserialize(fs) as TaskServerSettings);
result = true;
//}
//catch { }
}
return result;
}
public bool Save(string settingsFile = null)
{
if (settingsFile == null)
{
settingsFile = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location.TrimEnd('\\')) + #"\TaskServerSettings.xml";
}
bool result = false;
using (FileStream fs = new FileStream(settingsFile, FileMode.Create))
{
XmlSerializer xs = new XmlSerializer(this.GetType());
try
{
xs.Serialize(fs, this);
result = true;
}
catch { }
}
return result;
}
#endregion
public bool Equals(TaskServerSettings settingsToCompare)
{
if (this.Enabled != settingsToCompare.Enabled ||
this.CheckInterval != settingsToCompare.CheckInterval)
{
return false;
}
return true;
}
bool IEqualityComparer.Equals(object x, object y)
{
return x.Equals(y);
}
int IEqualityComparer.GetHashCode(object obj)
{
throw new NotSupportedException();
}
}
}
Writing the object with its default property values in the first run works pretty good.
The XML file looks like this then:
<?xml version="1.0"?>
<TaskServerSettings xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Enabled>true</Enabled>
<CheckInterval>5000</CheckInterval>
</TaskServerSettings>
However, deserializing the same file on the second run causes the error when it tries to load the file on
xs.Deserialize(fs) as TaskServerSettings.
InvalidOperationException - Error in XML document (0,0).
The root element is missing.
I already tried to avoid the static method and tried new as well as I already tried to remove the IEqualityComparer parent + the last three methods. Without success.
I wonder, whats the cause of this error?
When you execute this statement:
if (!xs.CanDeserialize(XmlReader.Create(fs)))
it starts reading the stream. So when you call Deserialize later, the stream is not at the start, so the deserialization fails. You need to rewind the stream by setting fs.Position = 0