7

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.

1 Answer 1

7

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.

Sign up to request clarification or add additional context in comments.

6 Comments

Huh? putting [KnownType(Type)] on a Type itself looks weird to me, but i give it a try.
Strange, but it does work indeed but only in one side. I have a public void Post([FromBody]IEnumerable<IMessage> messages) method to which i then pass the stream, and messages always null, I'm using Fiddler for this maybe my post query is incorrect, or serializer can't read what it wrote.
Yeah, it is not working when you post back the same xml. I guess because of the ArrayOfanyType and anyType. I'm currently fiddling with it. I will get back to you if I find something.
@AlexBurtsev I've only managed to get it work on POST with introducing a wrapper class: [KnownType(typeof(Message))] [KnownType(typeof(Message2))] public class MessagesHolder { public IEnumerable<IMessage> Messages { get; set; } } then you only need to annotate this with KnownType and if you return MessagesHolder from Get you can post the XML back. I think the problem is that without the wrapper class there is no place to put the KnownType so it can't deserialize into a plain IEnumerable<IMessage> in the POST.
@AlexBurtsev beside the workaround what I posted as a comment, I've updated my answer with the "proper" solution.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.