XML Serialization and Deserialization in C#

[Originally Posted By]: https://blog.udemy.com/csharp-serialize-to-xml/

Serialization is a mechanism for converting an object (such as an instance of a class, or a collection of objects) into a stream of bytes or characters that you can save to a file or database, or even send across the Internet to other systems. When needed, you can deserialize the data – converting it back to a usable object in memory. The .NET framework contains many classes to help with this process, and offers in-built support for XML serialization (serializing an object to an XML data file) through the XmlSerializer class and the System.Xml.Serialization library.

This article provides a brief overview of XML serialization and deserialization in the C# programming language. It assumes that readers have a reasonable knowledge of C# and Microsoft Visual Studio, and so complete beginners would benefit from exploring the fundamentals of C# programming first. Readers who are unfamiliar with XML should learn the basics of XML programming before continuing. This is especially important if intending to use serialization to exchange data with other systems.

Serializing XML in C#

Many .NET framework objects and classes can be serialized without adding any special directives or attributes to the code. By default, all public properties of a class are already serializable.

The example below defines a simple class in a Visual C# Console Application, and then serializes the contents to the console window.

/*
 * 
 * Udemy.com
 * XML Serialization and Deserialization in C#
 * 
*/

using System;
using System.Xml.Serialization;

namespace XMLTest1
{
    public class Test
    {
        public String value1;
        public String value2;
    }

    class Program
    {
        static void Main(string[] args)
        {
            Test myTest = new Test() { value1 = "Value 1", value2 = "Value 2" };
            XmlSerializer x = new XmlSerializer(myTest.GetType());
            x.Serialize(Console.Out, myTest);
            Console.ReadKey();
        }
    }
}

The actual serialization is done by an instance of the class XmlSerializer, from the System.Xml.Serialization namespace. The serializer’s constructor requires a reference to the type of object it should work with – which can be obtained by using the GetType() method of an instanced object, or a call to the function typeof()and specifying the class name as the only argument.

The Serialize() method takes an object of the defined type, translates that object into XML, and then writes the information to a defined stream (in this case, the TextWriter object of the console’s output stream). The XML output of the sample code is shown below:

<?xml version="1.0" encoding="ibm850"?>
<Test xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
 <value1>Value 1</value1>
 <value2>Value 2</value2>
</Test>

The names of elements and attributes in the XML output are set by the names of the properties and fields from the object.

You can direct the output of the serialization to a wide variety of .NET streams, including MemoryStream (with XmlWriter and StringWriter), FileStream, and NetworkStream classes. It is also possible to serialize an object into an XmlDocument with the help of an instance of XPathNavigator, as shown in the following example:

/*
 * 
 * Udemy.com
 * XML Serialization and Deserialization in C#
 * 
*/

using System;
using System.Xml;
using System.Xml.XPath;
using System.Xml.Serialization;

namespace XMLTest1
{
    public class Test
    {
        public String value1;
        public String value2;
    }

    class Program
    {
        static void Main(string[] args)
        {
            XmlDocument myXml = new XmlDocument();
            XPathNavigator xNav = myXml.CreateNavigator();
            Test myTest = new Test() { value1 = "Value 1", value2 = "Value 2" };
            XmlSerializer x = new XmlSerializer(myTest.GetType());
            using (var xs = xNav.AppendChild())
            {
                x.Serialize(xs, myTest);
            }
            Console.WriteLine(myXml.OuterXml);
            Console.ReadKey();
        }
    }
}

Deserializing XML Data

Deserialization is the process of taking XML-formatted data and converting it to a .NET framework object: the reverse of the process shown above. Providing that the XML is well-formed and accurately matches the structure of the target type, deserialization is a relatively straightforward task.

In the example below, the XML output of the preceding examples is hard-coded into a string, but it could be fetched from a network stream or external file. The XmlSerializer class is used to deserialize the string to an instance of the Test class, and the example then prints the fields to the console. To obtain a suitable stream that can be passed into the XmlSerializer’s constructor, a StringReader (from the System.IO namespace) is declared.

/*
 * 
 * Udemy.com
 * XML Serialization and Deserialization in C#
 * 
 */

using System;
using System.IO;
using System.Xml.Serialization;

namespace XMLTest1
{
    public class Test
    {
        public String value1;
        public String value2;
    }

    class Program
    {
        static void Main(string[] args)
        {
            String xData = "<?xml version=\"1.0\" encoding=\"ibm850\"?><Test xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"><value1>Value 1</value1><value2>Value 2</value2></Test>";            
            XmlSerializer x = new XmlSerializer(typeof(Test));
            Test myTest = (Test)x.Deserialize(new StringReader(xData));
            Console.WriteLine("V1: " + myTest.value1);
            Console.WriteLine("V2: " + myTest.value2);
            Console.ReadKey();
        }
    }
}

Serializing Lists and Collections

You can serialize arrays, generic lists, and other collection objects to XML, provided that their class implements ICollection or IEnumerable.

Simple arrays and generic lists generally work unmodified, and may appear identical in the final output. For example, whether objects are declared as an array of Test objects or as a generic list of Test objects, the XmlSerializer will write both using the same XML code:

<?xml version="1.0" encoding="ibm850"?>
<ArrayOfTest xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
 <Test>
    <value1>A1</value1>
    <value2>B1</value2>
 </Test>
 <Test>
    <value1>B1</value1>
    <value2>B2</value2>
 </Test>
</ArrayOfTest>

As a result, XML data in this format can be deserialized to either a generic list ofTest objects, or an array of Test objects. It is up to the programmer to specify which type of object should be used for deserialization.

Controlling the Serialization Using Attributes

When serializing data for exchange with other applications, or when working to a predefined XML schema, it is useful to be able to change the element and attribute names used during the process. By default, elements in the XML output are named after the properties or fields that they are based on. You can rename the root node using the XmlRoot attribute, and change the name of child nodes by using the XmlElement attribute and setting its ElementName.

Multiple properties can be specified for an attribute by separating them with commas within the parenthesis. This usually takes the form[attributename(property1=value1, property2=value2…)]

[XmlRoot("XTest")]
public class Test
{
    [XmlElement(ElementName="V1")]
    public String value1;

    [XmlElement(“V2")]
    public String value2;        
}

Note that when you are only specifying the element name, the property name can be omitted.

Adding the XmlElement attribute, as shown above, not only sets the name to be used, but it also tells the XmlSerializer to use an XML element for that field. You can change value1 to be an attribute of the XTest element by declaring the field with XmlAttribute instead.

Two different attributes are used for arrays and collections. XmlArray controls the root node of the list, and XmlArrayItem controls each element in that array.

[XmlRoot("XTest")]
public class Test
{
    [XmlElement(ElementName="V1")]
    public String value1;

    [XmlElement(ElementName="V2")]
    public String value2;

    [XmlArray("OtherValues")]
    [XmlArrayItem("OValue")]
    public List others = new List();
}

The example above is serialized to XML in a format similar to the following:

<?xml version="1.0" encoding="ibm850"?>
<XTest xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
 <V1>A1</V1>
 <V2>B1</V2>
 <OtherValues>
    <OValue>Test</OValue>
 </OtherValues>
</XTest>

In certain situations, you may want to exclude a public property or field from the output. This can be done by adding the attribute XmlIgnore to the property in the class’s declarations:

[XmlIgnore]
public String value2;
…

Finally, when working to a defined schema, it is often necessary to remove the standard namespace definitions that are added by the XmlSerializer. This is usually best handled when calling the Serialize() method of the XmlSerializer instance. An optional parameter for this method specifies the namespaces to be used, an XmlSerializerNamespaces collection, and can contain blank values.

XmlSerializer x = new XmlSerializer(myTest.GetType());
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("", "");
x.Serialize(Console.Out, myTest, ns);

Of course, namespaces can also be added using the same XmlSerializerNamespaces collection. However, it is often clearer to use theNamespace property of the XmlRoot attribute in combination with the code above.

By keeping in mind, or pre-planning, the serialization needs of the structures used in your application as you write the classes, it is possible to add object persistence and loading of external data files to the application with a very minimal amount of programming effort.

Overriding the Serialization of a Class

As mentioned earlier, all public properties and fields of a class are automatically serializable, and can usually be converted to XML without using any directives or attributes. Private properties and fields are not serialized by default. To include these, and for more precise control over how an object is serialized to XML, you can override the entire serialization process.

You do this by implementing IXmlSerializable in your classes, and including three methods that are required for the XML serialization to work: GetSchema(), WriteXml(), and ReadXml().

A thorough explanation of working with the XmlWriter and XmlReader classes used by these methods is beyond the scope of this article. Working with the data at such a level may draw on many different aspects of C# programming and a variety of technologies from the .NET framework. For more advanced C# information, C# 2012 Fundamentals at Udemy.com forms a complete course from beginner-level projects to advanced concepts, and contains more examples of serialization in Part III.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s