Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

Implementation of XML Serialization and Data Contract Serialization Utility in .NET

3.35/5 (16 votes)
19 Jun 2017CPOL5 min read 25K   280  
Basic Idea about DeSerializer and Serializer; advantage and disadvantages of XmlSerializer and DataContractSerializer; implementation XML and DataContract Serializer/ De-Serializer.

Implementation of XML Serialization and Data Contract Serialization Utility in .NET

Readers of the Topic - Developers

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

Image 1

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

Image 2

Project Creation

The problem can be solved many different ways. Right now, I am mainly focusing only on Xml Serialization and Data-Contract Serialization.

Image 3

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

Image 4

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.

Image 5

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

Image 6

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

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.

<?xml version='1.0' encoding='utf-16'?>
<Person>
    <PersonId>044373a4-0f17-4ec4-8e1f-ac6d1d7873e7</PersonId>
    <LastName>Rony</LastName>
    <FirstName>HR</FirstName> 
    <Address> 
        <AddressId>b90c16b4-418a-4eca-80fb-8679a60b418e</AddressId> 
        <City>City</City> 
        <State>State</State> 
        <ZipCode>1200</ZipCode> 
    </Address> 
</Person>

The full test method is given bellow and it should be passed.

[TestMethod]
public void Given_Valid_XML_String_For_Person_When_DeSerializer_Is_Called_Then_It_Should_Return_Valid_Person_Object()
{
    //// Arrange
    Person personObj;

    //// Positive Test Test Data
    string xmlData =
    @"<?xml version='1.0' encoding='utf-16'?>
    <Person>
        <PersonId>044373a4-0f17-4ec4-8e1f-ac6d1d7873e7</PersonId>
        <LastName>Rony</LastName>
        <FirstName>HR</FirstName>
        <Address>
            <AddressId>b90c16b4-418a-4eca-80fb-8679a60b418e</AddressId>
            <City>City</City>
            <State>State</State>
            <ZipCode>1200</ZipCode>
        </Address>
    </Person>";

    string expectedLastNamet = "Rony";

    //// Act
    personObj = xmlSerializerUtility.DeSerializer<Person>(xmlData);

    //// Assert.
    Assert.AreEqual(personObj.LastName, expectedLastNamet);
}

XML Serializer Testing

The serialize method is called inside the test method and it should be passed.

[TestMethod]
public void Given_Valid_PersonObj_When_Serialize_Is_Called_Then_It_Should_Return_Valid_XML_Data()
{
    //// Arrange
    Address address = new Address
    {
        AddressId = Guid.NewGuid(),
        City = "City",
        State = "State",
        ZipCode = "1200"
    };

    Person person = new Person
    {
        PersonId = Guid.NewGuid(),
        FirstName = "HR",
        LastName = "Rony",
        Address = address
    };

    //// Expected Result.
    string actualXmlData = null;

    //// Act
    actualXmlData = xmlSerializerUtility.Serializer<person>(person);

    //// Assert.
    Assert.IsNotNull(actualXmlData);
}

</person>

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. 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.
  • 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

Image 7

Look at the above codes, it is almost similar the previous implementation except DataContractSerializer Class.

Now say, I have xml string which has the name and education properties of an Employee.

<Employee> 
    <LastName>Rony</LastName> 
    <FirstName>HR</FirstName> 
    <EmployeeId>d0d54690-037f-473b-9eff-4e30e8e0ed4f</EmployeeId> 
    <Educations> 
        <Education> 
            <EducationId>748a5d33-2cda-454f-ab23-63f12ecccd76</EducationId> 
            <EmployeeId>d0d54690-037f-473b-9eff-4e30e8e0ed4f</EmployeeId> 
            <DegreeText>Bachelors in computer science</DegreeText> 
        </Education> 
        <Education> 
            <EducationId>9491fdbb-e1e8-4781-bb61-749e837a0b11</EducationId> 
            <EmployeeId>d0d54690-037f-473b-9eff-4e30e8e0ed4f</EmployeeId> 
            <DegreeText>Masters in computer science</DegreeText> 
        </Education> 
    </Educations> 
</Employee>

Look at the tag of the elements (LastName, FirstName, EmployeeId) in the xml strings which are not alphabetically ordered.

Problem Analysis

Image 8

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.

Image 9

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.

internal static class XmlSorter 
{ 
    internal static void Sort(this XElement xElement, bool sortAttributes = true) 
    { 
        //// Make sure there is a valid xElement 
        if (xElement == null) throw new ArgumentNullException("XElement is null");

        //// Sort attributes if needed 
        if (sortAttributes) 
        { 
            List <XAttribute> sortedAttributes = xElement.Attributes().OrderBy(a => a.ToString(), new CaseSensitiveComparer()).ToList(); 
            sortedAttributes.ForEach(a => a.Remove()); 
            sortedAttributes.ForEach(a => xElement.Add(a)); 
        }

        //// Sort the children if anything exist 
        List <XElement> sortedChildren = xElement.Elements().OrderBy(e => e.Name.ToString(), new CaseSensitiveComparer()).ToList(); 
        if (xElement.HasElements) 
        { 
            xElement.RemoveNodes(); 
            sortedChildren.ForEach(c => c.Sort(sortAttributes)); 
            sortedChildren.ForEach(c => xElement.Add(c)); 
        } 
    }

    internal static void RemoveEmptyElement(this XElement xElement) 
    { 
        //// Make sure there is a valid xElement 
        if (xElement == null) throw new ArgumentNullException("XElement is null");

        //// Remove Empty/Blanks elements in collection of XML nodes. 
        xElement.Descendants().Where(e => string.IsNullOrEmpty(e.Value)).Remove(); 
    } 
}

internal class CaseSensitiveComparer : IComparer 
        <string> 
{ 
    public int Compare(string x, string y) 
    { 
        return string.Compare(x, y, StringComparison.Ordinal); 
    } 
}

Sortable DataContract Serializer

Image 10

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

[TestMethod]

public void Given_Valid_XML_Data_For_Employee_When_DeSerializer_Is_Called_Then_It_Should_Return_Valid_Employee_Object()

{

    //// Arrange

    ISerialization sortabledataContractSerializer = new DataContractSortableSerializerUtility(dataContractSerializer);

    //// Valid Test Data

    Employee employeeObj;

    string xmlData = @"     
      <Employee> 
        <LastName>Rony</LastName> 
        <FirstName>HR</FirstName> 
        <EmployeeId>d0d54690-037f-473b-9eff-4e30e8e0ed4f</EmployeeId> 
        <Educations> 
            <Education> 
                <DegreeText>Bachelors in computer science</DegreeText> 
                <EducationId>748a5d33-2cda-454f-ab23-63f12ecccd76</EducationId>
                <EmployeeId>d0d54690-037f-473b-9eff-4e30e8e0ed4f</EmployeeId>
            </Education> 
            <Education> 
                <DegreeText>Masters in computer science</DegreeText> 
                <EducationId>9491fdbb-e1e8-4781-bb61-749e837a0b11</EducationId>
                <EmployeeId>d0d54690-037f-473b-9eff-4e30e8e0ed4f</EmployeeId>
            </Education> 
        </Educations> 
      </Employee>";

    int expectedCount = 2;

    //// Act 
    employeeObj = sortabledataContractSerializer.DeSerializer<Employee>(xmlData);

    //// Assert. 
    Assert.AreEqual(employeeObj.Educations.Count, expectedCount); 
}

The expected result from the test is given bellow -

Image 11

Serialization Testing

[TestMethod]
public void Given_Valid_EmployeeObj_When_Serialize_Is_Called_Then_It_Should_Return_Valid_XML_Data()
{
    //// Arrange
    ISerialization sortabledataContractSerializer = new DataContractSortableSerializerUtility(dataContractSerializer);

    //// Valid Test Data
    Employee employee = new Employee();
    employee.EmployeeId = Guid.NewGuid();
    employee.FirstName = "HR";
    employee.LastName = "Rony";
 
    List<Education> educationList = new List<Education>();
    Education education = new Education();
    education.EducationId = Guid.NewGuid();
    education.EmployeeId = employee.EmployeeId;
    education.DegreeText = "Bachelors in computer science";
    educationList.Add(education);
 
    Education educationObj2 = new Education();
    educationObj2.EducationId = Guid.NewGuid();
    educationObj2.EmployeeId = employee.EmployeeId;
    educationObj2.DegreeText = "Masters in computer science";
    educationList.Add(educationObj2);

    employee.Educations = educationList;

    //// Expected Result.
    string actualXmlData = null;

    //// Act
    actualXmlData = sortabledataContractSerializer.Serializer<Employee>(employee);

    //// Assert.
    Assert.IsNotNull(actualXmlData);
}

I 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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)