XML Serialization in C#

…is VERY poorly documented. So once and for all, let’s change that. XML parsing and XML serialization in C# is very easy, guys. You just need to write proper binding classes.  Plus you  need to know what the different C# attributes do. In this article I will simply provide a bunch of examples of different types of XML binding. Namely, elements, attributes, inline lists, etc.

First things first. Import System.Xml.Serialization

using System.Xml.Serialization;

Now let’s play by example… Given these classes:

public class MediaResources
{
    public List<Video> Videos { get; set; }
    public List<Image> Images { get; set; }
}
public class Video
{
    public string Title { get; set; }
    public string Description { get; set; }
    public DateTime Date { get; set; }
    public MediaElement MediaElement { get; set; }
}
public class MediaElement : Sizeable
{
    public string VideoCode { get; set; }
}
public class Image : Sizeable
{
    public string Filename { get; set; }
    public string Description { get; set; }
}
public class Sizeable
{
    public int Width { get; set; }
    public int Height { get; set; }
}

Make sure these classes are public, otherwise you will not be able to serialize them using XmlSerializer class, you will get this error: “XmlTests.MediaResources is inaccessible due to its protection level. Only public types can be processed“. Now, to serialize our MediaResources object (call it xml), we call this code:

var serializer = new XmlSerializer(xml.GetType());
serializer.Serialize(new StreamWriter("test.xml"), xml);

And get the following file:

<?xml version="1.0" encoding="utf-8"?>
<MediaResources xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Videos>
    <Video>
      <Title>Video 1</Title>
      <Description>single 'quotes'</Description>
      <Date>2012-11-20T00:33:22.104194-05:00</Date>
      <MediaElement>
        <Width>100</Width>
        <Height>50</Height>
        <VideoCode>H3274E92H32I</VideoCode>
      </MediaElement>
    </Video>
    <Video>
      <Title>Video 2</Title>
      <Description>double friggin "quotes"</Description>
      <Date>2012-11-19T23:33:22.105194-05:00</Date>
      <MediaElement>
        <Width>100</Width>
        <Height>50</Height>
        <VideoCode>1H23123JLK21</VideoCode>
      </MediaElement>
    </Video>
  </Videos>
  <Images>
    <Image>
      <Width>100</Width>
      <Height>50</Height>
      <Filename>img1.jpg</Filename>
      <Description>abcdefg</Description>
    </Image>
    <Image>
      <Width>100</Width>
      <Height>50</Height>
      <Filename>img2.jpg</Filename>
      <Description>sukasukasuka</Description>
    </Image>
  </Images>
</MediaResources>

Pretty right? Notice how the inherited Width and Height properties are serialized for both Image and MediaElement classes. Let’s make it prettier.

Element names

First of all, renaming the nodes. Let’s use the XmlElement attrubute for that:

public class Sizeable
{
    [XmlElement(ElementName = "my-width")]
    public int Width { get; set; }
    [XmlElement(ElementName = "my-height")]
    public int Height { get; set; }
}

And we get this XML:

<?xml version="1.0" encoding="utf-8"?>
<MediaResources xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  ...
  <Images>
    <Image>
      <my-width>100</my-width>
      <my-height>50</my-height>
      <Filename>img1.jpg</Filename>
      <Description>abcdefg</Description>
    </Image>
    <Image>
      <my-width>100</my-width>
      <my-height>50</my-height>
      <Filename>img2.jpg</Filename>
      <Description>sukasukasuka</Description>
    </Image>
  </Images>
</MediaResources>

Attributes

To make XML attributes, we use the XmlAttribute attribute like so:

public class Video
{
    [XmlAttribute(AttributeName = "my-title")]
    public string Title { get; set; }
    [XmlAttribute(AttributeName = "my-description")]
    public string Description { get; set; }
    public DateTime Date { get; set; }
    public MediaElement MediaElement { get; set; }
}

And we get this XML:

<?xml version="1.0" encoding="utf-8"?>
<MediaResources xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Videos>
    <Video my-title="Video 1" my-description="single 'quotes'">
      <Date>2012-11-20T00:50:39.941194-05:00</Date>
      <MediaElement>
        <my-width>100</my-width>
        <my-height>50</my-height>
        <VideoCode>H3274E92H32I</VideoCode>
      </MediaElement>
    </Video>
    <Video my-title="Video 1" my-description="double friggin &quot;quotes&quot;">
    ...
</MediaResources>

Notice how the serializer encodes double quotes with &quot; when shoving them into attributes.

Renaming arrays and lists

To rename the <Videos> and XML attributes, we use the XmlAttribute attribute like so:

public class MediaResources
{
    [XmlArray(ElementName = "my-list-of-videos")]
    [XmlArrayItem(ElementName = "my-child-video")]
    public List<Video> Videos { get; set; }
    [XmlArray(ElementName = "my-list-of-images")]
    [XmlArrayItem(ElementName = "my-child-image")]
    public List<Image> Images { get; set; }
}

And we get this XML:

<?xml version="1.0" encoding="utf-8"?>
<MediaResources xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <my-list-of-videos>
    <my-child-video my-title="Video 1" my-description="single 'quotes'">
      <Date>2012-11-20T00:58:32.070194-05:00</Date>
      <MediaElement>
        <my-width>100</my-width>
        <my-height>50</my-height>
        <VideoCode>H3274E92H32I</VideoCode>
      </MediaElement>
    </my-child-video>
    <my-child-video my-title="Video 1" my-description="double friggin &quot;quotes&quot;">
      <Date>2012-11-19T23:58:32.071194-05:00</Date>
      <MediaElement>
        <my-width>100</my-width>
        <my-height>50</my-height>
        <VideoCode>1H23123JLK21</VideoCode>
      </MediaElement>
    </my-child-video>
  </my-list-of-videos>
  <my-list-of-images>
    <my-child-image>
      <my-width>100</my-width>
      <my-height>50</my-height>
      <Filename>img1.jpg</Filename>
      <Description>abcdefg</Description>
    </my-child-image>
    <my-child-image>
      <my-width>100</my-width>
      <my-height>50</my-height>
      <Filename>img2.jpg</Filename>
      <Description>sukasukasuka</Description>
    </my-child-image>
  </my-list-of-images>
</MediaResources>

Inline lists

To completely remove the <Videos> (PLURAL!) element and leave the individual <Video> children repeat just by themselves, we can use the XmlElement attribute on the list itself. We will also remove the <Images> tag. Like so:

public class MediaResources
{
    [XmlElement(ElementName = "bastard-child-video")]
    public List<Video> Videos { get; set; }
    [XmlElement(ElementName = "bastard-child-image")]
    public List<Image> Images { get; set; }
}

And we get this XML:

<?xml version="1.0" encoding="utf-8"?>
<MediaResources xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <bastard-child-video my-title="Video 1" my-description="single 'quotes'">
    <Date>2012-11-20T01:00:02.001194-05:00</Date>
    <MediaElement>
      <my-width>100</my-width>
      <my-height>50</my-height>
      <VideoCode>H3274E92H32I</VideoCode>
    </MediaElement>
  </bastard-child-video>
  <bastard-child-video my-title="Video 1" my-description="double friggin &quot;quotes&quot;">
    <Date>2012-11-20T00:00:02.002194-05:00</Date>
    <MediaElement>
      <my-width>100</my-width>
      <my-height>50</my-height>
      <VideoCode>1H23123JLK21</VideoCode>
    </MediaElement>
  </bastard-child-video>
  <bastard-child-image>
    <my-width>100</my-width>
    <my-height>50</my-height>
    <Filename>img1.jpg</Filename>
    <Description>abcdefg</Description>
  </bastard-child-image>
  <bastard-child-image>
    <my-width>100</my-width>
    <my-height>50</my-height>
    <Filename>img2.jpg</Filename>
    <Description>sukasukasuka</Description>
  </bastard-child-image>
</MediaResources>

Renaming the root

To rename the root element, namely “MediaResources”, we use the XmlRoot attribute like so:

[XmlRoot(ElementName = "my-root")]
public class MediaResources
{    ...    }

And we get this XML:

<?xml version="1.0" encoding="utf-8"?>
<my-root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <bastard-child-video my-title="Video 1" my-description="single 'quotes'">
  ...
</my-root>

Nullable values

Suppose we set the value of string GeoCode in class MediaElement to null. We will get the following XML:

<MediaElement>
    <Id>578</Id>
    <Title>Le chant des étoiles</Title>
    <Description>Le cosmos est vivant...</Description>
    <MediaType>web_limelight</MediaType>
    <MogadorFileName>S0075294_100290089_005_8000k_dH264.mp4</MogadorFileName>
    <VideoCode>40066cb4a7c44c38a0057e7e3f006907</VideoCode>
</MediaElement>

But where is GeoCode? See, because it’s null, the default serialization omits it altogether. This behavior can be overridden, however, by setting parameter IsNullable=true of the respective XmlElement attribute. (NOTE: this applies to elements only. Attributes cannot be nullable)

[XmlElement(IsNullable = true)]
public string GeoCode { get; set; }

And here is the XML:

<MediaElement>
    <Id>578</Id>
    <Title>Le chant des étoiles</Title>
    <Description>Le cosmos est vivant...</Description>
    <MediaType>web_limelight</MediaType>
    <MogadorFileName>S0075294_100290089_005_8000k_dH264.mp4</MogadorFileName>
    <VideoCode>40066cb4a7c44c38a0057e7e3f006907</VideoCode>
    <GeoCode xsi:nil="true" />
</MediaElement>

Notice that it added xsi:nil=”true” to indicate that this value is, in fact, NULL and not an empty string. An important note here also is that you can NOT apply IsNullable=true to non-nullable types, AKA int, double, DateTime, struct, etc. You will get a System.InvalidOperationException when you call Serialize during runtime. However, you can apply IsNullable=true if you make those properties nullable, AKA int?, double?, DateTime?, [NAME-OF-WEIRD-STRUCT]?

Namespaces

To add namespaces to your XML, we tweak the Namespace property of any one of Xml serialization attributes (XmlElement, XmlAttribute, etc), passing the URL of the XML namespace we want. Like so:

[XmlRoot(ElementName = "my-root")]
public class MediaResources
{
    [XmlElement(ElementName = "bastard-child-video", Namespace = "http://mikhailisthebest")]
    public List<Video> Videos { get; set; }
    [XmlElement(ElementName = "bastard-child-image")]
    public List<Image> Images { get; set; }
}
public class Video
{
    [XmlAttribute(AttributeName = "my-title", Namespace = "http://mikhailisthebestinner")]
    public string Title { get; set; }
    [XmlElement(ElementName = "my-description", Namespace = "http://mikhailisthebestinner")]
    public string Description { get; set; }
    public DateTime Date { get; set; }
    public MediaElement MediaElement { get; set; }
}

Next, create an instance of XmlSerializerNamespaces class filled with namespaces and their prefixes. Add it to our Serialize call, like this:

var ns = new XmlSerializerNamespaces();
ns.Add("vid", "http://mikhailisthebest");
ns.Add("inner", "http://mikhailisthebestinner");
var serializer = new XmlSerializer(xml.GetType());
serializer.Serialize(new StreamWriter("test.xml"), xml, ns);

And we get this XML:

<?xml version="1.0" encoding="utf-8"?>
<my-root xmlns:vid="http://mikhailisthebest" xmlns:inner="http://mikhailisthebestinner">
  <vid:bastard-child-video inner:my-title="Video 1">
    <inner:my-description>single 'quotes'</inner:my-description>
    <vid:Date>2012-11-20T03:48:11.1651923-05:00</vid:Date>
    <vid:MediaElement>
      <vid:my-width>100</vid:my-width>
      <vid:my-height>50</vid:my-height>
      <vid:VideoCode>H3274E92H32I</vid:VideoCode>
    </vid:MediaElement>
  </vid:bastard-child-video>
  <vid:bastard-child-video inner:my-title="Video 1">
    <inner:my-description>double friggin "quotes"</inner:my-description>
    <vid:Date>2012-11-20T02:48:11.1661923-05:00</vid:Date>
    <vid:MediaElement>
      <vid:my-width>100</vid:my-width>
      <vid:my-height>50</vid:my-height>
      <vid:VideoCode>1H23123JLK21</vid:VideoCode>
    </vid:MediaElement>
  </vid:bastard-child-video>
  <bastard-child-image>
    <my-width>100</my-width>
    <my-height>50</my-height>
    <Filename>img1.jpg</Filename>
    <Description>abcdefg</Description>
  </bastard-child-image>
  <bastard-child-image>
    <my-width>100</my-width>
    <my-height>50</my-height>
    <Filename>img2.jpg</Filename>
    <Description>sukasukasuka</Description>
  </bastard-child-image>
</my-root>

Notice how the declaration of “inner” xmlns prefix on Title and Description overrides the parent “vid” xmlns prefix both in element and attribute.

Serializing TimeSpan

I never supposed C# would fail to serialize TimeSpan objects, since it serializes date objects pretty well. Still, here is a solution I found on StackOverflow:

[XmlIgnore]
public TimeSpan TimeIn { get; set; }

[Browsable(false)]
[XmlElement(DataType = "duration", ElementName = "TimeIn")]
public string TimeIn_str {
    get {
        return XmlConvert.ToString(TimeIn);
    }
    set {
        TimeIn = string.IsNullOrEmpty(value)
            ? TimeSpan.Zero
            : XmlConvert.ToTimeSpan(value);
    }
}

Here we use XmlIgnore attribute on our original property, TimeIn, to make the serializer skip it. We then create a new property, TimeIn_str, that is serializable as a string. We mark this new property with XmlElement attribute and give it the XML name of “TimeIn”. We also mark this new property Browsable(false) to hide it from the assembly browser… Not that I ever use it. The IntelliSense will still see it, unfortunately. #$%^&!

That’s it for now, I will post some more examples if I ever encounter other weird XML situations. Suggestions are always welcome!

Bonus: XmlFactory

Here is a neat utility class I like to use to speed my serialization/deserialization.

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

namespace MikhailIsTheBest
{
    public class XmlFactory
    {
        public static T LoadFromFile<T>(string filename) where T : class
        {
            T config = null;
            try
            {
                var reader = new StreamReader(filename);
                var serializer = new XmlSerializer(typeof(T));
                config = (T)serializer.Deserialize(reader);
                reader.Close();
            }
            catch (XmlException ex)
            {
                throw new Exception("XML Parse Error: " + ex.Message);
            }
            catch (InvalidOperationException ex)
            {
                throw new Exception("XML Serialization Error: " + ex.Message);
            }
            return config;
        }
        public static void WriteToFile<T>(T config, string filename)
        {
            var x = new XmlSerializer(config.GetType());
            var sw = new StreamWriter(filename);
            x.Serialize(sw, config);
            sw.Close();
        }
    }
}

Conclusion

I must say C# has a very powerful XML binding library and, hopefully, you can use these snippets to quickly hack some crazy XML file your boss gives to you. I know I will…  : )

Leave a Reply

Your email address will not be published.