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 which 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 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 into the project for the XML and data-contract serialization:

  • System.Xml.Serialization
  • System.Runtime.Serialization

I have also used System.IO namespace. We need the classes which are given bellow:

  • MemoryStrean
  • StreamWriter
  • StreamReader

Memory Stream Class

  • It stores data in memory (RAM)
  • It is used for fast and temporary storage

StreamWriter and StreamReader

These 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 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 the 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, dispose 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 implementation. So, you can use these deserialization method 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 micro service. Say, there is a client interface which will send data as 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 DataContractSerializer Class.

Now say, I have 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 FastName. 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 exist  
  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 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.

Up Next
    Ebook Download
    View all
    Learn
    View all