Make the XmlSerializer Your Friend
- May 18th, 2012
- Write comment
Over the years I have had to do a number of integrations with third party xml web services. These have included credit card payment gateways, government agencies, hotel booking engines and even game providers. Before all the cool kids jump in and say we should be using json, yes we should in a perfect world where we get to rewrite our software every time that the latest and greatest cool thing comes out. For those of us in the real world, these things still exist and need to be maintained and integrated with … enough said.
There are a few ways to consume xml, some good, some not. Lets have a look …
1) You can use XmlWriter and XmlReader classes to manually parse and write the xml. This method is error prone, time consuming, inefficient and difficult to read and maintain. Each time you modify the model object you also need to modify the serializer and deserializer. The one and only upside is that it gives you complete, utter control over the serialization and you can map to whatever object you like in one go. It is the most infinitely customisable way of consuming xml. I have never actually found the need to do it this way … I suggest you don’t either
2) You could use an XmlDocument object or even LinqToXml. This can be handy if dealing with a simple document or you only want one or two nodes out of a complex xml structure. In general this isn’t much better than manually parsing xml if you are trying to deserialize an entire object and is only a little less verbose. Still not a great idea except for the case where it just a node or two.
3) My preferred option … use the built in XmlSerializer given to you by Microsoft. Its not difficult to use and is easily customised using attributes on your model objects. At the end of the day, the xml is a model of data which can easily be translated into model objects.
Lets start with a sample xml that we want to deserialize. Its the standard cookie cutter person-with-an-address structure but its enough to give the basic idea …
<?xml version="1.0" encoding="utf-16"?> <people> <person dateadded="2007-09-24T08:00:00+02:00"> <firstname>Leonard</firstname> <surname>Hofstadter</surname> <address> <housenumber>12</housenumber> <street>Some Street</street> <suburb>Los Angeles</suburb> <state>California</state> </address> </person> <person dateadded="2007-09-24T07:00:00+02:00"> <firstname>Sheldon</firstname> <surname>Cooper</surname> <address> <housenumber>12-B</housenumber> <street>With Leonard</street> <suburb>Los Angeles</suburb> <state>California</state> </address> </person> </people> |
The nice touch about using the serializer is that it almost totally abstracts the fact that you are dealing with xml by mapping to a set of objects that model the data. Here are those models … we have a People class that contains a list of Person classes that each contain an Address class …
[XmlRoot("people")]
public class PeopleCollection
{
public PeopleCollection()
{
People = new List<Person>();
}
[XmlElement("person")]
public List<Person> People { get; set; }
}
[XmlType("person")]
public class Person
{
[XmlAttribute("dateadded")]
public DateTime DateAdded { get; set; }
[XmlElement("firstname")]
public string FirstName { get; set; }
[XmlElement("surname")]
public string LastName { get; set; }
[XmlElement("address")]
public Address Address { get; set; }
}
[XmlType("address")]
public class Address
{
[XmlElement("housenumber")]
public string HouseNumber { get; set; }
[XmlElement("street")]
public string Street { get; set; }
[XmlElement("suburb")]
public string Suburb { get; set; }
[XmlElement("state")]
public string State { get; set; }
} |
Nothing really that special … just a simple set of objects that represent the data contained in the xml. The decorators on each property and class tell the serializer how to serialize and deserialize the values. Does it output the property value as an attribute, an element or simply ignore it altogether? The decorators are optional .. if they are omitted the serializer will expect the xml to have the same elements (case sensitive) as the property names and all properties will be serialized as elements.
In this case I have purposely changed the cases of the xml and the property names to demonstrate the use of decorators to customise the serializer. You may note that in the Person class I have mapped the surname element to the LastName property by using the decorator and I have also specified that DateCreated should be emitted as an attribute. More information about the many decorators that can be used to control serialization can be found here.
From this point the serialization code is straightforward …
public class Serializer<T> where T : class
{
public string Serialize(T obj)
{
var serializer = new XmlSerializer(typeof (T));
var namespaces = new XmlSerializerNamespaces();
namespaces.Add(string.Empty, string.Empty);
var sb = new StringBuilder();
using (var writer = XmlWriter.Create(sb))
{
serializer.Serialize(writer, obj, namespaces);
}
return sb.ToString();
}
public T Deserialise(string xml)
{
var serialiser = new XmlSerializer(typeof (T));
using (var stream = new MemoryStream(Encoding.Unicode.GetBytes(xml)))
{
return serialiser.Deserialize(stream) as T;
}
}
} |
The full example solution can be downloaded from GitHub
Happy coding
