Web API uses the Json.Net formatter to serialise its JSON responses which allows you to customise the format of the generated JSON very easily for the entire application at startup using:
config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
This allows you resolve the issues between C# syntax preferring PascalCase and javascript based clients preferring camelCase. However setting this globally on the API without taking into consideration who the client request is actually coming from seems to assume that an API will only have 1 type of client and whatever you set for your API is just the way it has to be.
With multiple client types for my API's (javascript, iOS, Android, C#), I'm looking for a way to set the Json.Net SerializerSettings per request such that the client can request their preferred format by some means (perhaps a custom header or queryString param) to override the default.
What would be the best way to set per-request Json.Net SerializerSettings in Web API?
Use camel case for all JSON property names The camel case property naming policy: Applies to serialization and deserialization.
While lowerCamelCase and snake_case might be the two most popular formats for JSON object keys, some programming languages might have different conventions. For example, C# uses PascalCase for property names, and it's common for C# APIs to return JSON in this format.
If you want JsonSerializer class to use camel casing you can do the following: var options = new JsonSerializerOptions() { PropertyNamingPolicy = JsonNamingPolicy. CamelCase }; string json = JsonSerializer. Serialize(empList, options); return Ok(json);
CamelCasePropertyNamesContractResolver Class. Resolves member mappings for a type, camel casing property names.
With a bit of help from Rick Strahl's blog post on creating a JSONP media type formatter, I have come up with a solution that allows the API to dynamically switch from camelCase to PascalCase based on the client request.
Create a MediaTypeFormatter that derives from the default JsonMediaTypeFormatter and overrides the GetPerRequestFormatterInstance method. This is where you can implement your logic to set your serializer settings based on the request.
public class JsonPropertyCaseFormatter : JsonMediaTypeFormatter
{
private readonly JsonSerializerSettings globalSerializerSettings;
public JsonPropertyCaseFormatter(JsonSerializerSettings globalSerializerSettings)
{
this.globalSerializerSettings = globalSerializerSettings;
SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/json"));
SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/javascript"));
}
public override MediaTypeFormatter GetPerRequestFormatterInstance(
Type type,
HttpRequestMessage request,
MediaTypeHeaderValue mediaType)
{
var formatter = new JsonMediaTypeFormatter
{
SerializerSettings = globalSerializerSettings
};
IEnumerable<string> values;
var result = request.Headers.TryGetValues("X-JsonResponseCase", out values)
? values.First()
: "Pascal";
formatter.SerializerSettings.ContractResolver =
result.Equals("Camel", StringComparison.InvariantCultureIgnoreCase)
? new CamelCasePropertyNamesContractResolver()
: new DefaultContractResolver();
return formatter;
}
}
Note that I take a JsonSerializerSettings argument as a constructor param so that we can continue to use WebApiConfig to set up whatever other json settings we want to use and have them still applied here.
To then register this formatter, in your WebApiConfig:
config.Formatters.JsonFormatter.SerializerSettings.Converters.Add(new StringEnumConverter());
config.Formatters.JsonFormatter.SerializerSettings.NullValueHandling = NullValueHandling.Ignore;
config.Formatters.JsonFormatter.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.Local;
config.Formatters.Insert(0,
new JsonPropertyCaseFormatter(config.Formatters.JsonFormatter.SerializerSettings));
Now requests that have a header value of X-JsonResponseCase: Camel
will receive camel case property names in the response. Obviously you could change that logic to use any header or query string param you like.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With