Implementation Of XML Serialization And Data Contract Serialization Utility In .NET

Coverage Topic

  • Basic idea about de-serialization and serialization
  • Advantage and disadvantages of XML Serialization
  • Implementation of XML serialization
  • White Box testing of XML serialization
  • Advantage and disadvantages of data-contract-serialization
  • Problem Analysis
  • Implementation of sortable data-contract serialization
  • White-Box testing of data-contract serialization
Let's Drill Down the Basic Concept
 
 
The above diagram wants to say,
  • Deserialization: Transforming XML string to object.
  • Serialization: Transforming object to XML string.
XmlSerializer
  • Only Public Properties can be transformed.
  • It can serialize collections that can be inherited from IEnumerable and ICollection.
  • You don’t need to specify serialization attributes for every property that needs to be transformed. If you want to ignore to translate any property, then you have to use [XmlIgnore] attribute.
Limitation
  • It can serialize only property
  • Properties should have the set and get functionality. It can’t serialize private or read-only properties, indexers, and methods.
Class Diagram
 
 
Project Creation
 
The problem can be solved in many different ways. Right now, I am mainly focusing only on XML Serialization and Data-Contract Serialization.
 
 
Basic Concept
 
I have used the following references in the project for the XML and data-contract serialization:
  • System.Xml.Serialization
  • System.Runtime.Serialization
I have also used the System.IO namespace. We need classes which are given below:
  • MemoryStrean
  • StreamWriter
  • StreamReader
Memory Stream Class
  • It stores data in memory (RAM)
  • It is used for fast and temporary storage
StreamWriter and StreamReader
Both of the classes are used for reading and writing character-based data.
 
Flush with a StreamWriter
Flush is used to move information buffer to destination.
 

Implementation of XML Serializer

 
 
There is no doubt that these codes are okay. It can serialize the object to an XML string. You can use these codes without any problems.
 
Now let’s see another implementation with using block.
 
 
GetType vs. typeof:
 
Look at the above image, I have used the using block and these are not the major change in this implementation. Look at the no.1 (marked with red), I have used tag.GetType() instead of typeof(T). It is better to use GetType because of code safety.
 
In the first implementation of the image, when I am going to call serializer.Serialize(streamWriter, tag), sometimes XmlSerializer throws a runtime exception because of the typeof(T).
 
If you don’t get the error, because of using the typeof(T), then you can have different opinions. Anyway, I'm going to use GetType instead of typeof(T).
 
Benefits
  • Because of the using block, disposal methods are automatically called. You don't need to call it manually.
  • Even if, I did not use StreamWriter.Flush (No.2 in the codes of the first implementation).

Implementation of XML DeSerializer

 
 
Look at the above image; I have changed very silly things. You can use either one of the implementations. So, you can use these deserialization methods for transforming XML string to object.
 
White Box Testing of XML Serializer/De-Serializer
 
For the white-box testing, I am going to use Behavior Driven Development (BDD) for the naming convention of the test method.
 
Concept of BDD
  1. Given I am a beginner to the BDD technique, and I never use this technique before
  2. When I read this tutorial for BDD
  3. Then I have started to like it and finally, I learn it.
Note
 
I'm avoiding the details of the BDD to keep this simple.
 
XML DeSerializer Testing
I am writing a method to test the DeSerializer method.
 
Naming Convention of a Test Method
  1. Given_Valid_XML_String_For_Person_When_DeSerializer_Is_Called_Then_It_Should_Return_Valid_Person_Object()  
Say, I have an xml string which has the name and address of a person.
  1. <?xml version='1.0' encoding='utf-16'?>  
  2. <Person>  
  3.     <PersonId>044373a4-0f17-4ec4-8e1f-ac6d1d7873e7</PersonId>  
  4.     <LastName>Rony</LastName>  
  5.     <FirstName>HR</FirstName>  
  6.     <Address>  
  7.         <AddressId>b90c16b4-418a-4eca-80fb-8679a60b418e</AddressId>  
  8.         <City>City</City>  
  9.         <State>State</State>  
  10.         <ZipCode>1200</ZipCode>  
  11.     </Address>  
  12. </Person>  
The full test method is given bellow and it should be passed.
  1. /// <summary>  
  2. /// It varify the method of DeSerializer when we pass the valid Xml Data into the DeSerialize Method  
  3. /// and it should return valid object of Person.  
  4. /// </summary>  
  5. [TestMethod]  
  6. public void Given_Valid_XML_String_For_Person_When_DeSerializer_Is_Called_Then_It_Should_Return_Valid_Person_Object()  
  7. {  
  8.     //// Arrange  
  9.     Person personObj;  
  10.   
  11.     //// Positive Test Test Data  
  12.     string xmlData =  
  13.     @"<?xml version='1.0' encoding='utf-16'?>  
  14.     <Person>  
  15.         <PersonId>044373a4-0f17-4ec4-8e1f-ac6d1d7873e7</PersonId>  
  16.         <LastName>Rony</LastName>  
  17.         <FirstName>HR</FirstName>  
  18.         <Address>  
  19.         <AddressId>b90c16b4-418a-4eca-80fb-8679a60b418e</AddressId>  
  20.             <City>City</City>  
  21.             <State>State</State>  
  22.             <ZipCode>1200</ZipCode>  
  23.         </Address>  
  24.     </Person>";  
  25.   
  26.     string expectedLastNamet = "Rony";  
  27.   
  28.     //// Act  
  29.     personObj = xmlSerializerUtility.DeSerializer<Person>(xmlData);  
  30.   
  31.     //// Assert.  
  32.     Assert.AreEqual(personObj.LastName, expectedLastNamet);  
  33. }  
XML Serializer Testing
The serialize method is called inside the test method and it should be passed.
  1. [TestMethod]  
  2. public void Given_Valid_PersonObj_When_Serialize_Is_Called_Then_It_Should_Return_Valid_XML_Data()  
  3. {  
  4.     //// Arrange  
  5.     Address address = new Address  
  6.     {  
  7.         AddressId = Guid.NewGuid(),  
  8.         City = "City",  
  9.         State = "State",  
  10.         ZipCode = "1200"  
  11.     };  
  12.   
  13.     Person person = new Person  
  14.     {  
  15.         PersonId = Guid.NewGuid(),  
  16.         FirstName = "HR",  
  17.         LastName = "Rony",  
  18.         Address = address  
  19.     };  
  20.   
  21.     //// Expected Result.  
  22.     string actualXmlData = null;  
  23.   
  24.     //// Act  
  25.     actualXmlData = xmlSerializerUtility.Serializer<Person>(person);  
  26.   
  27.     //// Assert.  
  28.     Assert.IsNotNull(actualXmlData);  
  29. }   
The Problem
 
I’m implementing a microservice. Say, there is a client interface that will send data in XML format to the service. I have no control over the client interface. Somehow, I have figured out the elements/properties which I need for the model (entity) classes. But I have no control to change the order of the properties. After analyzing the XML as well as the entity classes, I have got the following issues,
  • Service will receive XML string which has un-sorted ordered elements.
  • Each time, it would come from different sources with different orders.
  • Existing model class contains IDictionary or IList.
I am focusing only on XML and Data Contract De-Serializer/Serializer.
 
DataContractSerializer
  • Public Properties and private fields can be transformed. But you have to mention [DataMember] attribute.
  • It doesn’t support XML attributes
  • It supports IList, IDictionary (HashTable) interface.
Limitation
  • XML should have to be alphabetically sorted.
Performance usually, it is 10% faster than XmlSerializer.
 

Implementation of DataContract Serializer

 
 
 
Look at the above codes, it is almost similar to the previous implementation except for DataContractSerializer Class.
 
Now say, I have an XML string which has the name and education properties of an Employee. 
  1. <Employee>  
  2.     <LastName>Rony</LastName>  
  3.     <FirstName>HR</FirstName>  
  4.     <EmployeeId>d0d54690-037f-473b-9eff-4e30e8e0ed4f</EmployeeId>  
  5.     <Educations>  
  6.         <Education>  
  7.             <EducationId>748a5d33-2cda-454f-ab23-63f12ecccd76</EducationId>  
  8.             <EmployeeId>d0d54690-037f-473b-9eff-4e30e8e0ed4f</EmployeeId>  
  9.             <DegreeText>Bachelors in computer science</DegreeText>  
  10.         </Education>  
  11.         <Education>  
  12.             <EducationId>9491fdbb-e1e8-4781-bb61-749e837a0b11</EducationId>  
  13.             <EmployeeId>d0d54690-037f-473b-9eff-4e30e8e0ed4f</EmployeeId>  
  14.             <DegreeText>Masters in computer science</DegreeText>  
  15.         </Education>  
  16.     </Educations>  
  17. </Employee>  
Look at the tag of the elements (LastName, FirstName, EmployeeId) in the XML strings which are not alphabetically ordered.
 
Problem Analysis
 
 
Again, if you look at the model class of the employee, then you will see that, at runtime, it is alphabetically ordered.
 
So, if I run the test, then I will get the invalid data in the object.
 
 
 
Look at the above object, it just gets the value of "LastName". Because in the employee model class, first, it gets the LastName before the LastName. So, it takes the value of the LastName from the XML and after that, it ignores values of the FirstName and others.
 
Solutions
  • Sort the XML elements.
  • Remove the empty tags too.
I have added two extension methods for sorting and removing empty elements of the XML string.
  1. internal static class XmlSorter  
  2. {  
  3.     /// <summary>  
  4.     /// This is an extention method for sorting the XML.  
  5.     /// </summary>  
  6.     /// <param name="xElement">Xml Element</param>  
  7.     /// <param name="sortAttributes">By Default, Sorting the Parent Attributes is true.</param>  
  8.     internal static void Sort(this XElement xElement, bool sortAttributes = true)  
  9.     {  
  10.         //// Make sure there is a valid xElement  
  11.         if (xElement == nullthrow new ArgumentNullException("XElement is null");  
  12.   
  13.         //// Sort attributes if needed  
  14.         if (sortAttributes)  
  15.         {  
  16.             List<XAttribute> sortedAttributes = xElement.Attributes().OrderBy(a => a.ToString(), new CaseSensitiveComparer()).ToList();  
  17.             sortedAttributes.ForEach(a => a.Remove());  
  18.             sortedAttributes.ForEach(a => xElement.Add(a));  
  19.         }  
  20.   
  21.         //// Sort the children if anything exists 
  22.         List<XElement> sortedChildren = xElement.Elements().OrderBy(e => e.Name.ToString(), new CaseSensitiveComparer()).ToList();  
  23.         if (xElement.HasElements)  
  24.         {  
  25.             xElement.RemoveNodes();  
  26.             sortedChildren.ForEach(c => c.Sort(sortAttributes));  
  27.             sortedChildren.ForEach(c => xElement.Add(c));  
  28.         }  
  29.     }  
  30.   
  31.     internal static void RemoveEmptyElement(this XElement xElement)  
  32.     {  
  33.         //// Make sure there is a valid xElement  
  34.         if (xElement == nullthrow new ArgumentNullException("XElement is null");  
  35.   
  36.         //// Remove Empty/Blanks elements in collection of XML nodes.  
  37.         xElement.Descendants().Where(e => string.IsNullOrEmpty(e.Value)).Remove();  
  38.     }  
  39. }  
  40.   
  41. /// <summary>  
  42. /// This is required for comparing when sorts xml.  
  43. /// This is case sensitive Comparing. ie, the'A' and 'a' are different.   
  44. /// </summary>  
  45. internal class CaseSensitiveComparer : IComparer<string>  
  46. {  
  47.     public int Compare(string x, string y)  
  48.     {  
  49.         return string.Compare(x, y, StringComparison.Ordinal);  
  50.     }  
  51. }  

Sortable DataContract Serializer

 
 
 
Look at the above codes, I have wrapped the previous implementation of the DataContractSerializerUtility class. I have called the RemoveEmptyElements extension method to remove the empty tags from the XML string.
 
After removing the empty tags, it sorts the tags of the XML strings by calling the sort extension method.
 

White Box Testing of DataContract Serializer/De-Serializer

 
Now if I run the following test, then it will be passed the test. Now, these methods are ready to use.
 
Deserialization Testing
  1. /// <summary>  
  2. /// It varify the method of DeSerializer when we pass the valid Xml Data into the DeSerialize Method  
  3. /// and it should return valid object of Employee.  
  4. /// </summary>  
  5. [TestMethod]  
  6. public void Given_Valid_XML_Data_For_Employee_When_DeSerializer_Is_Called_Then_It_Should_Return_Valid_Employee_Object()  
  7. {  
  8.     //// Arrange  
  9.     ISerialization sortabledataContractSerializer = new DataContractSortableSerializerUtility(dataContractSerializer);  
  10.     //// Valid Test Data  
  11.     Employee employeeObj;  
  12.   
  13.     string xmlData = @"  
  14.                 <Employee>  
  15.                     <LastName>Rony</LastName>  
  16.                     <FirstName>HR</FirstName>  
  17.                     <EmployeeId>d0d54690-037f-473b-9eff-4e30e8e0ed4f</EmployeeId>  
  18.                     <Educations>  
  19.                         <Education>  
  20.                             <DegreeText>Bachelors in computer science</DegreeText>  
  21.                             <EducationId>748a5d33-2cda-454f-ab23-63f12ecccd76</EducationId>  
  22.                             <EmployeeId>d0d54690-037f-473b-9eff-4e30e8e0ed4f</EmployeeId>  
  23.                         </Education>  
  24.                         <Education>  
  25.                             <DegreeText>Masters in computer science</DegreeText>  
  26.                             <EducationId>9491fdbb-e1e8-4781-bb61-749e837a0b11</EducationId>  
  27.                             <EmployeeId>d0d54690-037f-473b-9eff-4e30e8e0ed4f</EmployeeId>  
  28.                         </Education>  
  29.                     </Educations>  
  30.                 </Employee>";  
  31.   
  32.     int expectedCount = 2;  
  33.   
  34.     //// Act  
  35.     employeeObj = sortabledataContractSerializer.DeSerializer<Employee>(xmlData);  
  36.   
  37.     //// Assert.  
  38.     Assert.AreEqual(employeeObj.Educations.Count, expectedCount);  
  39. }   
The expected result from the test is given below,
 
 
 
Serialization Testing
  1. /// <summary>  
  2. /// It varify the method of serialization when we pass the valid object of data into the Serializer Method  
  3. /// and it should return valid xml Data.  
  4. ///</summary>  
  5. [TestMethod]  
  6. public void Given_Valid_EmployeeObj_When_Serialize_Is_Called_Then_It_Should_Return_Valid_XML_Data()  
  7. {  
  8.     //// Arrange  
  9.     ISerialization sortabledataContractSerializer = new DataContractSortableSerializerUtility(dataContractSerializer);  
  10.   
  11.     //// Valid Test Data  
  12.     Employee employee = new Employee();  
  13.     employee.EmployeeId = Guid.NewGuid();  
  14.     employee.FirstName = "HR";  
  15.     employee.LastName = "Rony";  
  16.   
  17.     List<Education> educationList = new List<Education>();  
  18.     Education education = new Education();  
  19.     education.EducationId = Guid.NewGuid();  
  20.     education.EmployeeId = employee.EmployeeId;  
  21.     education.DegreeText = "Bachelors in computer science";  
  22.     educationList.Add(education);  
  23.   
  24.     Education educationObj2 = new Education();  
  25.     educationObj2.EducationId = Guid.NewGuid();  
  26.     educationObj2.EmployeeId = employee.EmployeeId;  
  27.     educationObj2.DegreeText = "Masters in computer science";  
  28.     educationList.Add(educationObj2);  
  29.     employee.Educations = educationList;  
  30.   
  31.     //// Expected Result.  
  32.     string actualXmlData = null;  
  33.   
  34.     //// Act  
  35.     actualXmlData = sortabledataContractSerializer.Serializer<Employee>(employee);  
  36.   
  37.     //// Assert.  
  38.     Assert.IsNotNull(actualXmlData);  
  39. }  
I have tried to show you, how to find out the problem as well as how to fix it quickly. Find the source code and full project. It is attached.


Similar Articles