Using DataContracts with WCF Web API

A few folks have been asking if it is possible to serialize/deserialize using the DataContractSerializer. Rest assured, yes it is possible. Now whether or not it is the easiest/most intuitive model well that it is a different question. Winking smile

If you read to the end the post you will see a bunch of extensions which have been pushed to webapicontrib and make this all MUCH easier (at least I think). If you don’t jump you’ll read below what you need to do if you do it yourself.

It stars with the SetSerializer<T> method which is expose on the XmlMediaTypeFormatter. You can call this method and pass a DataContractSerializer for T. That tells the formatter use this serializer not the default. You need to give it the exact information, for example if you are returning List<Contact> then make T List<Contact> NOT T. If you want to support both, you need to register both.

To allow you to get to the formatter, the MediaTypeFormatterCollection exposes an XmlFormatter property. You might now be thinking, “Oh that’s easy, but how do I get a hold of the formatter collection?”

Good question. It depends on whether you are on the server or on the client.

On the Server

The easiest way to configure things on the server is to annotate your service/resource with a [DataContractFormat] attribute. Once you do then we will automatically use the DataContractSerializer.

If you do not want to annotate your service, the alternative option is to do it in code. If you are using HttpHostConfiguration you can get to the formatters by accessing the OperationHandlerFactory property on the config class. If you are using the fluent API, you’ll want to cast to HttpHostConfiguration in order to access it. For example see the code below

 var config = new HttpHostConfiguration();
var formatter = OperationHandlerFactory.XmlFormatter;

formatter.SetSerializer<Contact>(
  new DataContractSerializer(typeof(Contact)));
formatter.SetSerializer<List<Contact>>(
  new DataContractSerializer(typeof(List<Contact>)));

The code above is setting the formatter to use the DCS for Contact and List<Contact> both for reading and writing.

On the client

On the client side how you configure depends on if you are reading or writing. In either case both steps involve calling SetSerializer<T> on the XmlMediaTypeFormatter.

For reading you will create the formatter and pass it to the ReadAs<T> method of the HttpContent which is accessed off of the Content property on HttpResponseMessage.

 var formatter = new XmlMediaTypeFormatter();
formatter.SetSerializer<List<Contact>>>(
  new DataContractSerializer(typeof(List<Contact>))));
var contact = response.Content.ReadAs<T>(
  new List<MediaTypeFormatter> { formatter });

If you are writing to the request, you will access the formatters collection on the ObjectContent<T> instance.

 var content = new ObjectContent<Contact>();
content.Formatters.SetSerializer<Contact>(
  new DataContractSerializer(typeof(Contact)));
client.Post(content);

New extension methods in webapicontrib to make life easier

Now that we’ve seen how you can do it, we can wrap this up in a bunch of helper extension methods and life gets a whole lot easier. I did that and pushed them to webapicontrib Winking smile

Check the code below from the new DataContractExample in webapicontrib.

 var config = HttpHostConfiguration.Create();
config.UseDataContractSerializer<Contact>();

var host = new HttpConfigurableServiceHost<ContactsResource>(
  config, new Uri("https://localhost:8080/"));
host.Open();
var client = new HttpClient("https://localhost:8080/");

var newContact = new Contact {FirstName = "Brad", LastName = "Abrams"};

var content = new ObjectContent<Contact>(newContact,
  "application/xml").UseDataContractSerializer();
var response = client.Post("contacts", content);

response = client.Get("contacts");
var contacts = response.Content.ReadAsDataContract<Contact[]>();

foreach (var contact in contacts)
{
  Console.WriteLine(string.Format(
    "Contact: FirstName={0}, LastName={1}", 
    contact.FirstName, contact.LastName));
}
Console.ReadLine();

I’ve highlighted the important parts

  1. UseDataContractSerializer<T> on the config will set the formatter to serialize T using DCS. In order to make life easier the extension method also registers for List<T> and T[].
  2. For reading on the client call the ReadAsDataContract<T> extension method on HttpContent.
  3. For writing on the client create an ObjectContent<T> instance and call the UseDataContractSerializer() extension metho

Download the bits in wcfwebapi contrib here