13

I've a self host Web API with 2 controllers:

  • For controller 1, I need default DataContractSerializer (I'm exposing EF 5 POCO)
  • For controller 2, I need XmlFormatter with parameter UseXmlSerializer set to true (I'm exposing an XmlDocument)

I've tried to set formatters during controller initialization, but the configuration seems to be global, affecting all controllers:

public class CustomConfigAttribute : Attribute, IControllerConfiguration
{
    public void Initialize(HttpControllerSettings settings,
    HttpControllerDescriptor descriptor)
    {
        settings.Formatters.XmlFormatter.UseXmlSerializer = true;

    }
}

How can I solve this?

tugberk
  • 57,477
  • 67
  • 243
  • 335
user1824269
  • 633
  • 1
  • 9
  • 18

5 Answers5

10

You were very much on the right track. But you need to initallise a new instance of the XmlMediaTypeFormatter in your config attributes otherwise you will affect the global reference.

As you know, you need to create 2 attributes based on the IControllerConfiguration interface.

public class Controller1ConfigAttribute : Attribute, IControllerConfiguration
{
    public void Initialize(HttpControllerSettings controllerSettings,
                           HttpControllerDescriptor controllerDescriptor)
    {
        var xmlFormater = new XmlMediaTypeFormatter {UseXmlSerializer = true};

        controllerSettings.Formatters.Clear();
        controllerSettings.Formatters.Add(xmlFormater);
    }
}

public class Controller2ConfigAttribute : Attribute, IControllerConfiguration
{
    public void Initialize(HttpControllerSettings controllerSettings,
                           HttpControllerDescriptor controllerDescriptor)
    {
        var xmlFormater = new XmlMediaTypeFormatter();
        controllerSettings.Formatters.Clear();
        controllerSettings.Formatters.Add(xmlFormater);
    }
}

Then decorate your controllers with the relevant attribute

[Controller1ConfigAttribute]
public class Controller1Controller : ApiController
{

[Controller2ConfigAttribute]
public class Controller2Controller : ApiController
{
Mark Jones
  • 12,156
  • 2
  • 50
  • 62
1

Configuration:

config.Formatters.Remove(config.Formatters.JsonFormatter);
config.Formatters.Insert(0, new CustomXmlMediaTypeFormatter());

The Custom formatter:

public class CustomXmlMediaTypeFormatter : XmlMediaTypeFormatter
{
    public CustomXmlMediaTypeFormatter()
    {
        UseXmlSerializer = true;
    }
}

This seems to work, ok not so elegant. Removing default Xml Formatter does not work, so I concluded that the framework is somehow still using it.

user1824269
  • 633
  • 1
  • 9
  • 18
  • Been looking for your answer for several hours, it is so simple now. I just cant understand why this was so hard to find, I personally hate the DataContractSerializer. https://msdn.microsoft.com/en-us/library/system.net.http.formatting.xmlmediatypeformatter.usexmlserializer(v=vs.118).aspx – Christopher Bonitz May 25 '16 at 12:11
1

Mark Jones' answer has a big downside: By clearing all formatters it is not possible to request different ContentTypes and make use of the relevant formatter.

A better way to enable the XMLSerializer per Controller is to replace the default formatter.

public class UseXMLSerializerAttribute : Attribute, IControllerConfiguration
{
    public void Initialize(HttpControllerSettings controllerSettings, HttpControllerDescriptor controllerDescriptor)
    {
        // Find default XMLFormatter
        var xmlFormatter = controllerSettings.Formatters.FirstOrDefault(c => c.SupportedMediaTypes.Any(x => x.MediaType == "application/xml"));

        if (xmlFormatter != null)
        {
            // Remove default formatter
            controllerSettings.Formatters.Remove(xmlFormatter);
        }

        // Add new XMLFormatter which uses XmlSerializer
        controllerSettings.Formatters.Add(new XmlMediaTypeFormatter { UseXmlSerializer = true });
    }
}

And use it like this:

[UseXMLSerializer]
public TestController : ApiController
{
    //Actions
}
Rob
  • 4,927
  • 12
  • 49
  • 54
0

I think you could write a custom ActionFilterAttribute.

In OnActionExecuting, store away the original values in the HttpContext and then in OnActionExecuted, restore the original values.

Daniel A. White
  • 187,200
  • 47
  • 362
  • 445
  • I tried your solution but the formatter is shared by the controllers. I can receive two concurrent requests, one per controller, creating problems of inconsistency in the value of UseXmlSerializer. – user1824269 Nov 14 '12 at 18:06
0

the controllers actions themselves should not be concerned with how the data is serialized. yo should be able to request the data and any format necessary the operation to retrieve the data would be the same.

by default web api serialized to json objects. however if you set the content type of the request to xml is should return the same result, but formatted as xml instead of json.

Jason Meckley
  • 7,589
  • 1
  • 24
  • 45