Could somebody advise if XML Serialization is achievable with my design?
I have written the code for the core functionality for my 'System Administration' application. For Sprint 1 of my project, I have to store all data in a temporary XML data store which will be replaced using ADO.Net in Sprint 2.
I have looked into serializing classes using XML Serialization such as in below articles: https://www.codeproject.com/Articles/483055/XML-Serialization-and-Deserialization-Part https://www.codeproject.com/Articles/487571/XML-Serialization-and-Deserialization-Part-2
I have the following classes which (I think) need to be stored as XML:
I have the below code in my Program file:
static void Main()
{
UserAccessGroup SystemAdmin_App = new UserAccessGroup("Admin Operators");
UserAccessGroup Shareholder_App = new UserAccessGroup("Shareholders");
UserAccessGroup Broker_App = new UserAccessGroup("Brokers");
UserAccessGroup StockExMgr_App = new UserAccessGroup("StockExMgrs");
UserIDGenerator IDGenerator = new UserIDGenerator();
UserAdministration userAdmin = new UserAdministration(IDGenerator,
SystemAdmin_App, Shareholder_App, Broker_App, StockExMgr_App);
UserVerificationService userVerification = new UserVerificationService(
userAdmin, SystemAdmin_App);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
LoginScreen myLoginScreen = new LoginScreen();
Application.Run(myLoginScreen);
}
Only now that I have finished writing all of my classes using TDD have I looked into storing object data as XML. I am finding it very confusing to apply XML Serialization to my design and I am wondering if it is possible to achieve this without completely re-designing my application from scratch?
Any help here would be greatly appreciated!
As per the discussion in comments, I'll give you a basic overview of how you can quickly (de)serialize custom classes.
For reference, here's your comment to which I'm replying:
@Flater Thanks for the reply! I'm still unsure how to do serialize my objects. For example, UserAdministration contains a list of Users and has methods for adding/removing Users from the list. Say for example, I add a new user by calling the addUser method of UserAdministration. How does the new User object get added to the XML file?
I always use this helper class because it makes the code so much cleaner. I've copied it from the internet somewhere, potentially StackOverflow. I will credit the author once I know/remember who it is.
public static class SerializerHelper
{
/// <summary>
/// Serializes an object.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="serializableObject"></param>
/// <param name="fileName"></param>
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
}
}
/// <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
}
return objectOut;
}
}
Before I get to the usage examples, some tips so you know how to approach this:
Foo
, a List<Foo>
, or a custom made class that contains all the data you want to save MyFooData
Dictionary<T1,T2>
or any IEnumerable
that uses hashes for storage. However, if you want to store a Dictionary<T1,T2>
, you can store it as a List<KeyValuePair<T1,T2>>
, which means it will retain your data; just not the internal hash that it uses for quick lookup. 1 - The simplest example
[Serializable]
public class User
{
public string Name { get; set; }
public string Email { get; set; }
}
public static void TestMethod()
{
var myUser = new User() { Name = "John", Email = "John@john.com" };
//Save to file
SerializerHelper.SerializeObject(@"C:\MyDir\MyFile.txt", myUser);
//Read from file
var myUserReloaded = SerializerHelper.DeSerializeObject<User>(@"C:\MyDir\MyFile.txt");
}
Take note of the [Serializable]
attribute on the User
class. This is usually the only change that you need to make to your classes to make this work.
2 - Avoiding recursion and stack overflows
As mentioned in the tips, this serialization has a weakness to recursion. This can happen when classes refer to eachother in both directions.
[Serializable]
public class User
{
public string Name { get; set; }
public string Email { get; set; }
public Manager Boss {get; set; }
}
[Serializable]
public class Manager
{
public string Name { get; set; }
public User FavoriteEmployee {get; set; }
}
public static void TestMethod()
{
var userJohn = new User() { Name = "John", Email = "John@john.com" };
var managerMark = new Manager() { Name = "Mark" };
managerMark.FavoriteEmployee = userJohn;
userJohn.Boss = managerMark;
//Save to file
SerializerHelper.SerializeObject(@"C:\MyDir\MyFile.txt", userJohn);
//Read from file
var userJohnReloaded = SerializerHelper.DeSerializeObject<User>(@"C:\MyDir\MyFile.txt");
}
You will get a stack overflow exception when saving the file, because the serializer gets stuck in an infinite loop.
The serializer tries to write all of userJohn
's properties to the file. When it gets to the Boss
property, it notices that it is a serializable object and will begin serializing all of managerMark
's properties. When it gets to the FavoriteEmployee
property, it notices that it is a serializable object and will begin serializing all of userJohn
's properties. When it...
You can prevent this from happening by using the [XmlIgnore] attribute on one of the two properties (or both, if you want to be really secure).
[Serializable]
public class User
{
public string Name { get; set; }
public string Email { get; set; }
public Manager Boss {get; set; }
}
[Serializable]
public class Manager
{
public string Name { get; set; }
[XmlIgnore]
public User FavoriteEmployee {get; set; }
}
The serializer tries to write all of userJohn
's properties to the file. When it gets to the Boss
property, it notices that it is a serializable object and will begin serializing all of managerMark
's properties. When it gets to the FavoriteEmployee
property, it notices that this property is marked [XmlIgnore]
and it will not attempt to serialize what is contained within.
I hope this answer is what you were looking for.
EDIT I forgot a big caveat.
Let's say we are storing a List<Child>
with two Child
objects in it. Both Child
objects have a Parent
property, and it just so happens that both children are referencing the same Parent
(same object in memory).
If I store the Parent
(including a list of its children) it will serialize our Parent
which will contain both Child
objects. When deserializing, you will again have a singular Parent
object and the two Child
objects you started with.
If I store the List<Child>
(including their parent), it will serialize the Parent
for both Child
objects. However, when deserializing, both Parent
objects will be deserialized but they will be separate objects in memory .
As you can see, there could be a potential bug here if you are expecting both children to refer to the same Parent
(as an object in memory). For this reason, I have a personal rule about how to use the serialization, and where to put the [XmlIgnore]
attribute.
Children ( IEnumerable<Foo>
) get serialized, parents ( Foo
) do not. This allows me to store an entire tree in one go.
This means that I have to store the parent (and automatically have its children be serialized too), and I can only store the child without its reference to its parent (unless you store a ParentId
key in the Child
).
By doing it this way, you ensure that you won't create a multitude of Parent
objects through deserialization, because every object only gets mentioned once in the XML file.
Edit
I forgot to add the SerializerHelper.
to the method calls. Fixed now.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.