How to store C# object data as XML using XML Serialization - c#

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:
A User class with multiple properties
A UserAccessGroup class with a String property Name and a List of ints UserIDs
A UserAdministration Class that holds a List of all User objects and one or more UserAccessGroup objects (constructor uses params UserAccessGroup[]). It has methods for adding/deleting users on the system, adding/deleting UserIDs from a UserAccessGroup.
A UserVerification Class that holds a UserAdministration object and the UserAccessGroup object for the application being logged on to. It also has two ints for counting failed and successful logon attempts a method for logging on which returns true if all conditions are met, otherwise false.
A ServiceRequest class with multiple properties
A ServiceRequestTracker class that holds a list of all ServiceRequest objects and has methods for adding/deleting/editing them.
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:
These methods will serialize a single object (of any type) to a given file. This can be a class Foo, a List<Foo>, or a custom made class that contains all the data you want to save MyFooData
You cannot add to an existing file. When saving a file, the old file will be overwritten. I have not yet come across a use case where I needed to add to a file and couldn't just read the file's contents, change them, and store the whole object again. But I've only used it for small scale storage.
XML serialization uses public properties and a parameterless constructor. So make sure the objects you want to store have both of these. It will not serialize private or protected properties; or public/private/protected class fields.
The XML Serializer will automatically also serialize subclasses that are contained within (if they are public properties!). As far as I'm aware, it can go as deep as you want it to go. It will save everything.
The XML serializer has a weakness to recursion. I will explain how to avoid recursion in the code examples.
XML Serialization cannot (de)serialize 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.

Related

Managing multiple versions of object in JSON

I have a class in C#, that has a number of variables. Let's call it "QuestionItem".
I have a list of this object, which the user modifies, and then sends it via JSON serialization (with Newtonsoft JSON library) to the server.
To do so, I deserialize the objects that are already in the server, as a List<QuestionItem>, then add this new modified object to the list, and then serialize it back to the server.
In order to display this list of QuestionItems to the user, I deserialize the JSON as my object, and display it somewhere.
Now, the problem is - that I want to change this QuestionItem and add some variables to it.
But I can't send this NewQuestionItem to the server, because the items in the server are of type OldQuestionItem.
How do I merge these two types, or convert the old type to the new one, while the users with the old version will still be able to use the app?
You are using an Object Oriented Language, so you might aswell use inheritance if possible.
Assuming your old QuestionItem to be:
[JsonObject(MemberSerialization.OptOut)]
public class QuestionItem
{
[JsonConstructor]
public QuestionItem(int Id, int Variant)
{
this.Id = Id;
this.Variant = Variant;
}
public int Id { get; }
public int Variant { get; }
public string Name { get; set; }
}
you can extend it by creating a child class:
[JsonObject(MemberSerialization.OptOut)]
public class NewQuestionItem : QuestionItem
{
private DateTime _firstAccess;
[JsonConstructor]
public NewQuestionItem(int Id, int Variant, DateTime FirstAccess) : base(Id, Variant)
{
this.FirstAccess = FirstAccess;
}
public DateTime FirstAccess { get; }
}
Note that using anything different than the default constructor for a class requires you to use the [JsonConstructor] Attribute on this constructor and every argument of said constructor must be named exactly like the corresponding JSON properties. Otherwise you will get an exception, because there is no default constructor available.
Your WebAPI will now send serialized NewQuestionItems, which can be deserialized to QuestionItems. In fact: By default, JSON.NET as with most Json libraries, will deserialize it to any object if they have at least one property in common. Just make sure that any member of the object you want to serialize/desreialize can actually be serialized.
You can test the example above with the following three lines of code:
var newQuestionItem = new NewQuestionItem(1337, 42, DateTime.Now) {Name = "Hello World!"};
var jsonString = JsonConvert.SerializeObject(newQuestionItem);
var oldQuestionItem = JsonConvert.DeserializeObject<QuestionItem>(jsonString);
and simply looking at the property values of the oldQuestionItem in the debugger.
So, this is possible as long as your NewQuestionItem only adds properties to an object and does neither remove nor modify them.
If that is the case, then your objects are different and thus, requiring completely different objects with a different URI in your API, as long as you still need to maintain the old instance on the existing URI.
Which brings us to the general architecture:
The most clean and streamline approach to what you are trying to achieve is to properly version your API.
For the purpose of this link I am assuming an Asp.NET WebApi, since you are handling the JSON in C#/.NET. This allows different controller methods to be called upon different versions and thus, making structural changes the resources your API is providing depending on the time of the implementation. Other API will provide equal or at least similar features or they can be implemented manually.
Depending on the amount and size of the actual objects and potential complexity of the request- and resultsets it might also be worth looking into wrapping requests or responses with additional information. So instead of asking for an object of type T, you ask for an Object of type QueryResult<T> with it being defined along the lines of:
[JsonObject(MemberSerialization.OptOut)]
public class QueryResult<T>
{
[JsonConstructor]
public QueryResult(T Result, ResultState State,
Dictionary<string, string> AdditionalInformation)
{
this.Result = result;
this.State = state;
this.AdditionalInformation = AdditionalInformation;
}
public T Result { get; }
public ResultState State { get; }
public Dictionary<string, string> AdditionalInformation { get; }
}
public enum ResultState : byte
{
0 = Success,
1 = Obsolete,
2 = AuthenticationError,
4 = DatabaseError,
8 = ....
}
which will allow you to ship additional information, such as api version number, api version release, links to different API endpoints, error information without changing the object type, etc.
The alternative to using a wrapper with a custom header is to fully implement the HATEOAS constraint, which is also widely used. Both can, together with proper versioning, save you most of the trouble with API changes.
How about you wrapping your OldQuestionItem as a property of QuestionItem? For example:
public class NewQuestionItem
{
public OldQuestionItem OldItem { get; set; }
public string Property1 {get; set; }
public string Property2 {get; set; }
...
}
This way you can maintain the previous version of the item, yet define new information to be returned.
Koda
You can use something like
public class OldQuestionItem
{
public DateTime UploadTimeStamp {get; set;} //if less then DateTime.Now then it QuestionItem
public string Property1 {get; set; }
public string Property2 {get; set; }
...
public OldQuestionItem(NewQuestionItem newItem)
{
//logic to convert new in old
}
}
public class NewQuestionItem : OldQuestionItem
{
}
and use UploadTimeStamp as marker to understand, what Question is it.

XmlSerializer exception with large amount of objects

I am storing DLL information into the following class:
[XmlRoot("Modules")]
[XmlInclude(typeof(Module))]
public class Modules : List<Module> {}
[XmlType("Module")]
public class Module
{
[XmlIgnore()]
public bool Selected { get; set; }
[XmlElement()]
public string Name { get; set; }
[XmlElement()]
public string Version { get; set; }
[XmlElement()]
public byte[] Binary;
}
When I serialize a handful of Module classes in the list using the following code, it is fine.
XmlSerializer serializer = new XmlSerializer(typeof(Modules));
using (StringWriter sw = new StringWriter())
{
serializer.Serialize(sw, ugModules.DataSource);
}
However, my problem comes when within the list, I have 113 items - now obviously that is quite a lot of data with the binary.
I am getting a System.OutOfMemory exception, I seem to remember having to increase a threshold of some sort but I can't find the correct property or method for what exactly I have to change.
I am serializing this to XML to pass the XML into SQL and then store the information within a table, just for a bit of scope on what exactly I am doing.
How do I get around the exception? Is there a better way to do this?

C# save/retrieve an array of structures from settings file

I have a simple question (I think) that I'm not making much progress finding an answer to with Google. I have a structure as follows:
/// <summary>
/// A class to represent the sync settings for a single camera.
/// </summary>
public class CameraSyncSettings
{
public string ID { get; set; }
public string SyncPath { get; set; }
public bool OverwriteExisting { get; set; }
};
And then an array of these in the program, one for each camera:
List<CameraSyncSettings> MyCameraSettings = new List<CameraSyncSettings>();
Now, what I want to do is have a property in my settings such that I can read/write this array into it to persist the information between sessions.
How can I do this and what is the best/most efficient way?
You can achieve it by using Properties.Settings of type ListDictionary
Example:
Properties.Settings.Default.Example.Add("Setting1", new CameraSyncSettings());
Properties.Settings.Default.Example.Add("Setting2", new CameraSyncSettings());
Properties.Settings.Default.Example.Add("Setting3", new CameraSyncSettings());
Properties.Settings.Default.Save();
see link for more information : http://msdn.microsoft.com/en-us/library/aa730869(v=vs.80).aspx
NB: You can set the Scope of Properties.Settings.Default.Example to Application or User
As we've cleared in the comments you want to use app.config: this project should pretty much cover all your needs.

.NET serialization of JSON data with dynamic structure (schema)

The web service I consume responces with json data.
it gives resultObject as array:
resultObject:[{object1}, {object2},...] if there more then one object
and it returns
resultObject:{object1} if there only one object.
for serializing in .NET I created a "static" structure of classes to map json schema. But if in one case i've got an array (list) of objects an in other case just one object, how is it possible to handle this situation?
I have found a plethora of ugly solutions to this one, but so far goes:
If you use the System.Web.Script.Serialization.JavaScriptSerializer you have very limited control. If the result data type is simple, you could simply use the DeserializeObject method; it will translate everything into Dictionary and the "resultObject" property will in the first case be a Dictionary while the latter case will turn it into an array of such dictionary. It will not save you the headache of the final translation, but you will get the data into dictionaries which could be considered a first step.
I also attempted to use the KnownTypes and the DataContractJsonSerializer, but alas the datacontract serializer needs "hints" in the form of specially named properties to aid it deserializing. (Why am I using the KnownType attribute wrong?). This is a hopeless strategy if you don't have any control of the serialization which I guess is the case for you.
So now we are down to the butt-ugly solutions of which trial-and-error is my first choice:
When using the ScriptSerializer the conversion will fail with an InvalidOperationException if anything is wrong. I then created two data types one with data-as-arrays and one where data is a single instance (the DataClass is my invention since you do not specify the data types):
[DataContract]
public class DataClass
{
[DataMember]
public string FirstName { get; set; }
[DataMember]
public int BirthYear { get; set; }
public override string ToString()
{
return "FirstName : '" + FirstName + "', BirthYear: " + BirthYear;
}
}
[DataContract]
public class ResultSingle
{
[DataMember]
public DataClass Data { get; set; }
}
[DataContract]
public class ResultArray
{
[DataMember]
public List<DataClass> Data { get; set; }
}
Using these data types it is possible to translate using
JavaScriptSerializer jSer = new JavaScriptSerializer();
var one = jSer.Deserialize<ResultSingle>(jsonWithSingleInstance);
var many = jSer.Deserialize<ResultArray>(jsonWithArray);
But this of course require you to known the data type in advance and you get two different result types. Instead you could choose to always convert to ResultArray. If you get an exception you should convert as ResultSingle and then instantiate the ResultArray and manually add the data object to the Data list:
static ResultArray ConvertJson(string json)
{
ResultArray fromJson;
JavaScriptSerializer jSer = new JavaScriptSerializer();
try
{
fromJson = jSer.Deserialize<ResultArray>(json);
return fromJson;
}
catch (InvalidOperationException)
{
var single = jSer.Deserialize<ResultSingle> (json);
fromJson = new ResultArray();
fromJson.Data = new List<DataClass>();
fromJson.Data.Add(single.Data);
return fromJson;
}
}
static void Main(string[] args)
{
var jsonWithSingleInstance = "{\"Data\":{\"FirstName\":\"Knud\",\"BirthYear\":1928}}";
var jsonWithArray = "{\"Data\":[{\"FirstName\":\"Knud\",\"BirthYear\":1928},{\"FirstName\":\"Svend\",\"BirthYear\":1930}]}";
var single = ConvertJson(jsonWithSingleInstance);
var array = ConvertJson(jsonWithArray);
}
I do not say this is a beautiful solution (it isn't), but it should do the job.
You could also look at json.net which seem to be the json serializer of choice in .NET: How to install JSON.NET using NuGet?
Well, my service provider finally said that it is really a bug.
http://jira.codehaus.org/browse/JETTISON-102
says that is it because of java version that they use.

C#, problem mixing Xml Serialization with Nhibernate

I am working on a program that uses Nhibernate to persist objects, and Xml Serialization to import and export data. I can't use the same properties for collections as, for example, Nhibernate needs them to be Ilists, because it has it's own implementation of that interface, and I can't Serialize interfaces. But as I need both properties to be synchronized, I thought I could use two different properties for the same Field. The properties will be according to what I need for each framework, and they will update the Field accrodingly.
So, I have the following field:
private IList<Modulo> modulos;
And the following properties:
[XmlIgnore]
public virtual IList<Modulo> Modulos
{
get { return modulos; }
set { modulos = value; }
}
[XmlArray]
[XmlArrayItem(typeof(Modulo))]
public virtual ArrayList XmlModulos
{
get
{
if (modulos == null) return new ArrayList();
var aux = new ArrayList();
foreach (Modulo m in modulos)
aux.Add(m);
return aux;
}
set
{
modulos = new List<Modulo>();
foreach (object o in value)
modulos.Add((Modulo)o);
}
}
The first one is working perfectly, being quite standard, but I have some problems with the second one. The get is working great as I am having no problems Serializing objects (meaning it correctly takes the info from the field). But when I need to Deserialize, it is not getting all the info. The debugger says that after the Deserialization, the field is not updated (null) and the Property is empty (Count = 0).
The obvious solution would be using two unrelated properties, one for each framework, and passing the information manually when needed. But the class structure is quite complicated and I think there should be a more simple way to do this.
Any Idea how I can modify my property for it to do what I want? Any help will be appreciated.
The short answer is that you cant.
Typically you would create a DTO ( Data transfer object ) separate from your NHibernate objects. For example:
public class PersonDto
{
[XmlAttribute(AttributeName = "person-id")]
public int Id { get; set; }
[XmlAttribute(AttributeName = "person-name")]
public string Name{ get; set; }
}
On your DTO object you only put the properties that you intend to serialize. You than create a DTO from your domain model when you need to serialize one.
There is a great little library called automapper that makes mapping from your domain objects to your dto's pretty straight forward. See: http://automapper.codeplex.com/
Here is an example of a person class that supports mapping to the above DTO.
public class Person
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
static Person()
{
Mapper.CreateMap<PersonDto, Person>();
Mapper.CreateMap<Person, PersonDto>();
}
public Person(PersonDto dto)
{
Mapper.Map<PersonDto, Person>(dto, this);
}
public PersonDto ToPersonDto()
{
var dto = new PersonDto();
Mapper.Map<Person, PersonDto>(this, dto);
return dto;
}
}

Categories

Resources