1

i want to change the incoming requests deserializing format just for one of my controllers. so i added this in to my Global.asax and it works just fine:

HttpConfiguration config = GlobalConfiguration.Configuration;
config.Formatters.JsonFormatter.SerializerSettings = new Newtonsoft.Json.JsonSerializerSettings
{
    ContractResolver = new DefaultContractResolver
    {
        NamingStrategy = new SnakeCaseNamingStrategy()
    }
};

but it apply the changes to all of the controllers. i just want apply it for one of my controllers. i also found this answer and i wrote this code according to that:

public void Initialize(HttpControllerSettings controllerSettings, HttpControllerDescriptor controllerDescriptor)
{
    var formatter = controllerSettings.Formatters.OfType<JsonMediaTypeFormatter>().Single();
    controllerSettings.Formatters.Remove(formatter);

    formatter = new JsonMediaTypeFormatter
    {
        SerializerSettings = { ContractResolver = new DefaultContractResolver { NamingStrategy = new SnakeCaseNamingStrategy() } }
    };

    controllerSettings.Formatters.Add(formatter);
}

but unfortunately it works just for serializing the outputs. is there a way to define it for deserializing inputs?

CodeFuller
  • 30,317
  • 3
  • 63
  • 79
Mohammad
  • 2,724
  • 6
  • 29
  • 55

1 Answers1

4

You can do what you need with a tricky media type formatter. Usually custom formatter overrides methods CanReadType() / CanWriteType() and ReadFromStreamAsync() / WriteToStreamAsync(). CanWriteType() in your case should always return false since you are not intersted in customizing serialization. As regards deserialization you could use standard JsonMediaTypeFormatter (through inheritance or aggregation) and set its SerializerSettings to use SnakeCaseNamingStrategy:

public class SnakeCaseJsonFormatter : JsonMediaTypeFormatter
{
    public SnakeCaseJsonFormatter()
    {
        SerializerSettings = new JsonSerializerSettings
        {
            ContractResolver = new DefaultContractResolver
            {
                NamingStrategy = new SnakeCaseNamingStrategy()
            }
        };
    }

    public override bool CanWriteType(Type type)
    {
        return false;
    }

    public override Task WriteToStreamAsync(Type type, object value, Stream writeStream, HttpContent content, TransportContext transportContext)
    {
        throw new NotImplementedException();
    }
}

Remaining part is applying of such custom formatter on controller level. You could do this with a custom attribute implementing IControllerConfiguration interface. In Initialize() method set your custom formatter at first position so that it takes precedence over standard JsonMediaTypeFormatter. You should not remove standard JsonMediaTypeFormatter because it will handle data serializing:

public sealed class SnakeCaseNamingAttribute : Attribute, IControllerConfiguration
{
    public void Initialize(HttpControllerSettings controllerSettings, HttpControllerDescriptor controllerDescriptor)
    {
        controllerSettings.Formatters.Insert(0, new SnakeCaseJsonFormatter());
    }
}

Now just apply this attribute on controller you want and voila:

[SnakeCaseNaming]
public class ValuesController : ApiController
CodeFuller
  • 30,317
  • 3
  • 63
  • 79
  • Thank you so much dear @CodeFuller. i did exactly what you posted but it didn't worked!! did you test the code? i guess we miss something. – Mohammad Nov 26 '17 at 05:09
  • Yes, I've checked and it works for me. What exactly does not work now? Have you removed customization of SerializerSettings in other places you tried? – CodeFuller Nov 26 '17 at 05:16
  • SnakeCaseNamingStrategy doesn't apply to my inputs. i didn't remove ant thing. i just created a two classes (SnakeCaseJsonFormatter and SnakeCaseNamingAttribute) and apply the attribute on my controller. i can post my code but i guess its so obvious. – Mohammad Nov 26 '17 at 05:24
  • when i add SnakeCaseNamingStrategy in my global class it changes the properties name to snake case. for example FirstName become first-name. but when i add your attribute on controller. it didn't change anything. – Mohammad Nov 26 '17 at 05:27
  • Try setting breakpoints in `SnakeCaseNamingAttribute.Initialize()` and `SnakeCaseJsonFormatter` constructor. Are they called? – CodeFuller Nov 26 '17 at 05:29
  • You wrote "it changes the properties name to snake case. for example FirstName become first-name". Are we still talking about deserializing? You send json with snake case naming and expect it to deserialize to usual naming, correct? – CodeFuller Nov 26 '17 at 05:33
  • i add log to those methods and both called when i open mt help file: – Mohammad Nov 26 '17 at 05:34
  • [SnakeCaseJsonFormatter..ctor line:24;[];[here] – Mohammad Nov 26 '17 at 05:34
  • [SnakeCaseNamingAttribute.Initialize line:12;[];[here] – Mohammad Nov 26 '17 at 05:34
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/159830/discussion-between-david-and-codefuller). – Mohammad Nov 26 '17 at 05:35
  • Thank you so much dear @CodeFuller. – Mohammad Nov 26 '17 at 05:53