1

We have found that using an attribute on the controller we can have the controllers default json formatter replaced :-

public class WcsControllerConfigAttribute : Attribute, IControllerConfiguration
{
    public void Initialize(HttpControllerSettings controllerSettings, HttpControllerDescriptor controllerDescriptor)
    {
        var mediaType = ConfigurationManager.AppSettings["WcsMediaType"];
        if (mediaType == "application/json")
        {
            var formatter = controllerSettings.Formatters.OfType<JsonMediaTypeFormatter>().Single();
            controllerSettings.Formatters.Remove(formatter);

            formatter = new JsonMediaTypeFormatter
            {
                SerializerSettings =
                {
                    ContractResolver = new NullableValueContractResolver(), 
                    NullValueHandling = NullValueHandling.Include, 
                    DefaultValueHandling = DefaultValueHandling.Populate
                }
            };

            controllerSettings.Formatters.Add(formatter);
        }

    }
}

But this seems to have the effect of replacing the formatter not only for the result we wish to return but also for the json body of the incoming request. Is there a way to replace only the formatter for the response and not the request?

Edit: OK In response the doctors remark I think I should possibly state what my ultimate goal is because then perhaps you can offer an even better solution than using the formatter.

We are implementing a RESTful server with a partner. Their system (based on websphere) sends us perfectly standard JSON except that everything is enclosed in quotes. The standard formatter/parser does not miss a beat and happily converts all the incoming quoted values to dates, decimals, booleans, etc. We are not looking to change this behaviour.

The responses are also standard JSON but they also want all the values to be quoted. So in our data transfer object everything that is not either a child object or an array is a string and gets serialised correctly most of the time. But a further limitation is that all the properties that are null have to be treated differently to how most systems would treat them. Namely:

  • Null strings are passed as empty string. "property":"" and not "property":null
  • Null arrays are passed as empty arrays. "array":[] and not "array":null
  • Null child objects are passed as an empty object. "object":{} and not "object":null

And we are not allowed to simply omit them either. Not that that would be easy I suspect.

I hope that makes the requirement a little clearer. Our solution to use the formatter was based on another question Json Convert empty string instead of null and this works well for what it's worth but stumbles only because it tries to apply the rules in both directions.

Community
  • 1
  • 1
naskew
  • 2,091
  • 1
  • 19
  • 20
  • Sounds like [XY Problem](http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem). Why you want to have different formatter? – Hamid Pourjam Dec 22 '15 at 17:14
  • 1
    Are you trying to deserialize just a specific thing? Or what exactly is the end goal? You can pass custom serializers to NewtonSoft and use JsonConvert – Matti Price Dec 22 '15 at 17:15

2 Answers2

1

You can write a custom MediaTypeFormatter to achieve what you need.

An official tutorial on how to write custom media-type formatters is available in this link http://www.asp.net/web-api/overview/formats-and-model-binding/media-formatters

Doing so will allow you to perform an http request using JSON and return a different media-type than JSON.

Keep in mind that when doing so the client needs to send the desired media-type on the request (in the accept header).

            ICredentials credentials = CredentialCache.DefaultCredentials;
            NetworkCredential credential = credentials.GetCredential(uri, "Basic");


            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
            request.Credentials = credential;

            request.Method = "GET";
            request.Headers.Add("api-version", "1.0");
            request.Headers.Add("customer-code", code);
            request.Headers.Add("Authorization", AuthUser);
            request.ContentType = "application/json"; //The request is sent in JSON
            request.Accept = "text/plain"; //You ask the server to return the response in the format you need it

            HttpWebResponse response = (HttpWebResponse) request.GetResponse();

            Stream receiveStream = response.GetResponseStream();
hivo
  • 291
  • 2
  • 5
  • I don't want to return a different type than JSON. I just need to adhere to some restrictions that the websphere system imposes with regard to how the JSON is used. Personally I see this as a problem with websphere but my opinion does not matter as we cannot change how websphere works. – naskew Dec 24 '15 at 09:33
  • I'm going to mark this as the answer because it is ultimately what we did. However the real answer is that actually this is not using MVC but working around it and the functionality I need is only available in MVC 6. – naskew Jan 07 '16 at 12:30
0

YES - You can..

You can define 2 different implementation of MediaTypeFormatter. Set the properties CanReadType() & CanWriteType() as per your choice. Then implement WriteToStream() & ReadFromStream() in both formatters. Finally register both of them for your specific route.

Satinder Sidhu
  • 373
  • 3
  • 9
  • If I understand you correctly I'd need to add all the types we are trying to parse for responses to the list of CanWriteType() of one formatter and similarly for the other parser all the types for the CanReadType(). I'd just point out that we are talking about 10's of classes in each direction and a partner that keeps changing their mind so this might become a maintenance nightmare but it sounds like it might be possible. – naskew Dec 23 '15 at 06:37
  • Actually, Satinder, perhaps I can do something by adorning the types that we can deal with with an attribute. That would make the maintenance issue less complex, we just have to be sure that each class has the attribute and then the CanReadType or CanWriteType can detect this on the type. – naskew Dec 24 '15 at 09:36