How to register Known Types for serialization when using ASP.NET MVC Web API

Alex Burtsev picture Alex Burtsev · Jan 25, 2013 · Viewed 9.1k times · Source

I have a ASP.NET MVC Web API controller that returns public IEnumerable<IMessage> Get()

It throws exception that I need to register types that derive from IMessage in known types collection passed to DataContractSerializer.

How to register "Known Types" for using with DataContractSerializer and DataContractJSONSerializer in MVC Web API project?

KnownType attribute cannot be placed on interface.

Answer

nemesv picture nemesv · Jan 25, 2013

You need to put the KnownTypeAttribute on your IMessage implementations:

public interface  IMessage
{
    string Content { get; }
}

[KnownType(typeof(Message))]
public class Message : IMessage {
    public string Content{ get; set; }
}

[KnownType(typeof(Message2))]
public class Message2 : IMessage
{
    public string Content { get; set; }
}

So when calling the following action:

 public IEnumerable<IMessage> Get()
 {
     return new IMessage[] { new Message { Content = "value1" }, 
                             new Message2 { Content = "value2" } };
 }

The result will be this:

<ArrayOfanyType xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
    <anyType xmlns:d2p1="http://schemas.datacontract.org/2004/07/MvcApplication3.Controllers" i:type="d2p1:Message">
        <d2p1:Content>value1</d2p1:Content>
    </anyType>
    <anyType xmlns:d2p1="http://schemas.datacontract.org/2004/07/MvcApplication3.Controllers" i:type="d2p1:Message2">
       <d2p1:Content>value2</d2p1:Content>
    </anyType>
</ArrayOfanyType>

But this will only work one "way". So you cannot post back the same XML.

In order to the following action should work:

public string Post(IEnumerable<IMessage> messages)

You need to register the known types globally, with configuring a DataContractSerializer and setting up in the GlobalConfiguration.Configuration.Formatters

GlobalConfiguration.Configuration
                   .Formatters
                   .XmlFormatter.SetSerializer<IEnumerable<IMessage>>(
                       new DataContractSerializer(typeof(IEnumerable<IMessage>), 
                           new[] { typeof(Message), typeof(Message2) }));

With using the configuration you don't need the KnownTypeAttribute on your implementation types.