41

Overriding the default JSON serializer settings for web API on application level has been covered in a lot of SO threads. But how can I configure its settings on action level? For example, I might want to serialize using camelcase properties in one of my actions, but not in the others.

Johan
  • 35,120
  • 54
  • 178
  • 293

3 Answers3

76

Option 1 (quickest)

At action level you may always use a custom JsonSerializerSettings instance while using Json method:

public class MyController : ApiController
{
    public IHttpActionResult Get()
    {
        var settings = new JsonSerializerSettings
        {
            ContractResolver = new CamelCasePropertyNamesContractResolver()
        };
        var model = new MyModel();
        return Json(model, settings);
    }
}

Option 2 (controller level)

You may create a new IControllerConfiguration attribute which customizes the JsonFormatter:

public class CustomJsonAttribute : Attribute, IControllerConfiguration 
{
    public void Initialize(HttpControllerSettings controllerSettings, HttpControllerDescriptor controllerDescriptor)
    {
        var formatter = controllerSettings.Formatters.JsonFormatter;

        controllerSettings.Formatters.Remove(formatter);

        formatter = new JsonMediaTypeFormatter
        {
            SerializerSettings =
            {
                ContractResolver = new CamelCasePropertyNamesContractResolver()
            }
        };

        controllerSettings.Formatters.Insert(0, formatter);
    }
}

[CustomJson]
public class MyController : ApiController
{
    public IHttpActionResult Get()
    {
        var model = new MyModel();
        return Ok(model);
    }
}
Federico Dipuma
  • 17,655
  • 4
  • 39
  • 56
  • do we need to instantiate a new formmater? can't we tweak the current instance or the change will be applied everywhere? – JobaDiniz Jan 19 '18 at 17:43
  • 2
    @JobaDiniz as you stated, if you change properties of the original formatter, those changes will affect every other controller – Federico Dipuma Jan 19 '18 at 17:47
  • Nice initialization of SerializerSettings! I didn't know about that C# feature. – gius Feb 01 '18 at 10:38
15

Here's an implementation of the above as Action Attribute:

public class CustomActionJsonFormatAttribute : ActionFilterAttribute
{
    public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
    {
        if (actionExecutedContext?.Response == null) return;

        var content = actionExecutedContext.Response.Content as ObjectContent;

        if (content?.Formatter is JsonMediaTypeFormatter)
        {
            var formatter = new JsonMediaTypeFormatter
            {
                SerializerSettings =
                {
                    ContractResolver = new CamelCasePropertyNamesContractResolver()
                }
            };

            actionExecutedContext.Response.Content = new ObjectContent(content.ObjectType, content.Value, formatter);
        }
    }
}

public class MyController : ApiController
{
    [CustomActionJsonFormat]
    public IHttpActionResult Get()
    {
        var model = new MyModel();
        return Ok(model);
    }
}
Dimitri
  • 6,923
  • 4
  • 35
  • 49
  • I used this code but I gave it a `Type ContractResolverType` property and then did `ContractResolver = (IContractResolver)Activator.CreateInstance(ContractResolverType)` so I don't have to create one of these for each contract resolver type. In use it looks like this: `[ActionContractResolver(ContractResolverType = typeof(DefaultContractResolver))]` – R. Salisbury Mar 09 '21 at 19:11
0

I needed to return a 404 status error code alongside a json object with error details. I solved it using WebApi.Content with a new new JsonMediaTypeFormatter.

public class MyController : ApiController
{
    public IHttpActionResult Get()
    {
        // Configure new Json formatter
        var formatter = new JsonMediaTypeFormatter
        {
            SerializerSettings =
            {
                TypeNameHandling = TypeNameHandling.None,
                PreserveReferencesHandling = PreserveReferencesHandling.None,
                Culture = CultureInfo.InvariantCulture,
                Formatting = Formatting.Indented,
                NullValueHandling = NullValueHandling.Ignore
            }
        };

        try
        {
            var model = new MyModel();
            return Content(HttpStatusCode.OK, model, formatter);
        }
        catch (Exception err)
        {
            var errorDto = GetErrorDto(HttpStatusCode.NotFound, $"{err.Message}");
            return Content(HttpStatusCode.NotFound, errorDto, formatter);
        }
    }
}
Javier Rojano
  • 814
  • 9
  • 17