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.
Related
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 have added XML Serialization into my project in order to store my object data in XML files.
I have used the following helper class to achieve this:
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;
}
}
Inside my methods, anywhere that I create/delete/alter objects, I use code such as the below to serialize the object data:
//Increments the failed logon attempts counter
public void incrementFailedLogonAttempts()
{
logonAttemptCounter.incrementFailedLogons();
//Update the failed logon attempt counter in the XML Data Store
SerializerHelper.SerializeObject(#"C:\Users\Michael"
+ #"\Google Drive\FDM Dev Course Content\Workspace\SystemAdmin\SystemAdmin\"
+ #"XML Data Store\LogonAttemptCounter.xml", logonAttemptCounter);
}
However, after running all my unit tests, some of my XML files (which were serialized fine before running all tests) now look like this:
<?xml version="1.0"?>
<ObjectProxy_4 xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
Does anyone know what might be going wrong here? Or why it is going wrong?
Any help would be greatly appreciated!
I think I know actually. My tests use mock objects to mock dependencies. In many of my tests, I have created the object whose method is to be tested, injecting mock objects in the constructor. Is this what is causing the problem? I am serializing mock objects, which by definition are empty?
I'm basically looking for someone to point me in the right direction on this. I read through some of the Microsoft documentation, but that wasn't very helpful. This is my first attempt at working with XML.
I'm writing an application that needs to store a list of known users and a list of aliases created by each of those users. I've figured out how to have my lists serialized and stored to XML files when the application closes and have it retrieve those when the application opens again, but I don't want to keep the list of users and aliases in memory.
In order for this to work, I need to have the ability to search, edit, and append to the XML file during run time.
The XML structure I envision is something like:
<UserRecord>
<User>Username-1</User>
<AliasRecord>
<Alias>Alias-1</Alias>
<Number>6135551234</Number>
</AliasRecord>
<AliasRecord>
<Alias>Alias-2</Alias>
<Number>6131238888</Number>
</AliasRecord>
</UserRecord>
Each user would only have one username but could have multiple aliases. I need to have the ability to add users, add aliases to a new or existing user, and change existing aliases. Usernames would never change, but an entire User record could be deleted.
So far, the only XML I've done in C# uses serialization but I don't think that approach will work for the above.
private void WriteXML()
{
try
{
System.Xml.Serialization.XmlSerializer XMLwriter = new System.Xml.Serialization.XmlSerializer(typeof(MessageRecord));
System.IO.StreamWriter XMLfile = new System.IO.StreamWriter("Saved MessageRecords.xml");
foreach (MessageRecord mr in OutgoingMessages)
{
XMLwriter.Serialize(XMLfile, mr);
}
XMLfile.Close();
}
catch (Exception e)
{
MessageBox.Show(e.Message);
}
}
Create two classes to represent UserRecord and AliasRecord.
public class UserRecord
{
public string User { get; set; }
public List<AliasRecord> AliasRecords { get; set; }
}
public class AliasRecord
{
public string Alias { get; set; }
public string Number { get; set; }
}
Populate them like this:
var userRecord = new UserRecord
{
User = "UserName1",
AliasRecord = new List<AliasRecord> {
new AliasRecord { Alias = "Alias1", Number = "12345678" },
new AliasRecord { Alias = "Alias2", Number = "23456789" }
}
};
And use this code to serialize/deserialize it:
public static class XmlHelper
{
public static bool NewLineOnAttributes { get; set; }
/// <summary>
/// Serializes an object to an XML string, using the specified namespaces.
/// </summary>
public static string ToXml(object obj, XmlSerializerNamespaces ns)
{
Type T = obj.GetType();
var xs = new XmlSerializer(T);
var ws = new XmlWriterSettings { Indent = true, NewLineOnAttributes = NewLineOnAttributes, OmitXmlDeclaration = true };
var sb = new StringBuilder();
using (XmlWriter writer = XmlWriter.Create(sb, ws))
{
xs.Serialize(writer, obj, ns);
}
return sb.ToString();
}
/// <summary>
/// Serializes an object to an XML string.
/// </summary>
public static string ToXml(object obj)
{
var ns = new XmlSerializerNamespaces();
ns.Add("", "");
return ToXml(obj, ns);
}
/// <summary>
/// Deserializes an object from an XML string.
/// </summary>
public static T FromXml<T>(string xml)
{
XmlSerializer xs = new XmlSerializer(typeof(T));
using (StringReader sr = new StringReader(xml))
{
return (T)xs.Deserialize(sr);
}
}
/// <summary>
/// Deserializes an object from an XML string, using the specified type name.
/// </summary>
public static object FromXml(string xml, string typeName)
{
Type T = Type.GetType(typeName);
XmlSerializer xs = new XmlSerializer(T);
using (StringReader sr = new StringReader(xml))
{
return xs.Deserialize(sr);
}
}
/// <summary>
/// Serializes an object to an XML file. (Fixed code)
/// </summary>
public static void ToXmlFile(object obj, string filePath)
{
var xs = new XmlSerializer(obj.GetType());
var ns = new XmlSerializerNamespaces();
var ws = new XmlWriterSettings { Indent = true, NewLineOnAttributes = NewLineOnAttributes, OmitXmlDeclaration = true };
ns.Add("", "");
using (XmlWriter writer = XmlWriter.Create(filePath, ws))
{
xs.Serialize(writer, obj, ns);
}
}
/// <summary>
/// Deserializes an object from an XML file.
/// </summary>
public static T FromXmlFile<T>(string filePath)
{
StreamReader sr = new StreamReader(filePath);
try
{
var result = FromXml<T>(sr.ReadToEnd());
return result;
}
catch (Exception e)
{
throw new Exception("There was an error attempting to read the file " + filePath + "\n\n" + e.InnerException.Message);
}
finally
{
sr.Close();
}
}
}
Example usage:
var result = XmlHelper.ToXml(userRecord);
Result:
<UserRecord>
<User>Username1</User>
<AliasRecords>
<AliasRecord>
<Alias>Alias1</Alias>
<Number>12345678</Number>
</AliasRecord>
<AliasRecord>
<Alias>Alias2</Alias>
<Number>23456789</Number>
</AliasRecord>
</AliasRecords>
</UserRecord>
I'm basically looking for someone to point me in the right direction on this. I read through some of the Microsoft documentation, but that wasn't very helpful. This is my first attempt at working with XML.
I'm writing an application that needs to store a list of known users and a list of aliases created by each of those users. I've figured out how to have my lists serialized and stored to XML files when the application closes and have it retrieve those when the application opens again, but I don't want to keep the list of users and aliases in memory.
In order for this to work, I need to have the ability to search, edit, and append to the XML file during run time.
The XML structure I envision is something like:
<UserRecord>
<User>Username-1</User>
<AliasRecord>
<Alias>Alias-1</Alias>
<Number>6135551234</Number>
</AliasRecord>
<AliasRecord>
<Alias>Alias-2</Alias>
<Number>6131238888</Number>
</AliasRecord>
</UserRecord>
Each user would only have one username but could have multiple aliases. I need to have the ability to add users, add aliases to a new or existing user, and change existing aliases. Usernames would never change, but an entire User record could be deleted.
So far, the only XML I've done in C# uses serialization but I don't think that approach will work for the above.
private void WriteXML()
{
try
{
System.Xml.Serialization.XmlSerializer XMLwriter = new System.Xml.Serialization.XmlSerializer(typeof(MessageRecord));
System.IO.StreamWriter XMLfile = new System.IO.StreamWriter("Saved MessageRecords.xml");
foreach (MessageRecord mr in OutgoingMessages)
{
XMLwriter.Serialize(XMLfile, mr);
}
XMLfile.Close();
}
catch (Exception e)
{
MessageBox.Show(e.Message);
}
}
Create two classes to represent UserRecord and AliasRecord.
public class UserRecord
{
public string User { get; set; }
public List<AliasRecord> AliasRecords { get; set; }
}
public class AliasRecord
{
public string Alias { get; set; }
public string Number { get; set; }
}
Populate them like this:
var userRecord = new UserRecord
{
User = "UserName1",
AliasRecord = new List<AliasRecord> {
new AliasRecord { Alias = "Alias1", Number = "12345678" },
new AliasRecord { Alias = "Alias2", Number = "23456789" }
}
};
And use this code to serialize/deserialize it:
public static class XmlHelper
{
public static bool NewLineOnAttributes { get; set; }
/// <summary>
/// Serializes an object to an XML string, using the specified namespaces.
/// </summary>
public static string ToXml(object obj, XmlSerializerNamespaces ns)
{
Type T = obj.GetType();
var xs = new XmlSerializer(T);
var ws = new XmlWriterSettings { Indent = true, NewLineOnAttributes = NewLineOnAttributes, OmitXmlDeclaration = true };
var sb = new StringBuilder();
using (XmlWriter writer = XmlWriter.Create(sb, ws))
{
xs.Serialize(writer, obj, ns);
}
return sb.ToString();
}
/// <summary>
/// Serializes an object to an XML string.
/// </summary>
public static string ToXml(object obj)
{
var ns = new XmlSerializerNamespaces();
ns.Add("", "");
return ToXml(obj, ns);
}
/// <summary>
/// Deserializes an object from an XML string.
/// </summary>
public static T FromXml<T>(string xml)
{
XmlSerializer xs = new XmlSerializer(typeof(T));
using (StringReader sr = new StringReader(xml))
{
return (T)xs.Deserialize(sr);
}
}
/// <summary>
/// Deserializes an object from an XML string, using the specified type name.
/// </summary>
public static object FromXml(string xml, string typeName)
{
Type T = Type.GetType(typeName);
XmlSerializer xs = new XmlSerializer(T);
using (StringReader sr = new StringReader(xml))
{
return xs.Deserialize(sr);
}
}
/// <summary>
/// Serializes an object to an XML file. (Fixed code)
/// </summary>
public static void ToXmlFile(object obj, string filePath)
{
var xs = new XmlSerializer(obj.GetType());
var ns = new XmlSerializerNamespaces();
var ws = new XmlWriterSettings { Indent = true, NewLineOnAttributes = NewLineOnAttributes, OmitXmlDeclaration = true };
ns.Add("", "");
using (XmlWriter writer = XmlWriter.Create(filePath, ws))
{
xs.Serialize(writer, obj, ns);
}
}
/// <summary>
/// Deserializes an object from an XML file.
/// </summary>
public static T FromXmlFile<T>(string filePath)
{
StreamReader sr = new StreamReader(filePath);
try
{
var result = FromXml<T>(sr.ReadToEnd());
return result;
}
catch (Exception e)
{
throw new Exception("There was an error attempting to read the file " + filePath + "\n\n" + e.InnerException.Message);
}
finally
{
sr.Close();
}
}
}
Example usage:
var result = XmlHelper.ToXml(userRecord);
Result:
<UserRecord>
<User>Username1</User>
<AliasRecords>
<AliasRecord>
<Alias>Alias1</Alias>
<Number>12345678</Number>
</AliasRecord>
<AliasRecord>
<Alias>Alias2</Alias>
<Number>23456789</Number>
</AliasRecord>
</AliasRecords>
</UserRecord>
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;
})())
{ }
}