Reputation: 1101
Our application uses validation attributes to make use of the ASP.NET model validation, however this gives dot separated names for validation errors. When passed through the CamelCasePropertyNamesContractResolver
this only applies camelcase to before the first dot, whereas we would like the have camelcase applied to each section of the name.
For example we currently get the current json response:
{
"body.State": [
"The state field is required."
],
"body.LatestVersion": [
"The latestVersion field is required."
]
}
But desire to get out:
{
"body.state": [
"The state field is required."
],
"body.latestVersion": [
"The latestVersion field is required."
]
}
In our MVC setup we do have a line similar to
services.AddJsonOptions(options => options.ContractResolver = new CamelCasePropertyNamesContractResolver());
We'd appreciate any solution, be that modifications to how we set up the resolver, or how we could modify the validation.
Edit: Just for reference the model structure for the request that is generating this request is as follows:
public sealed class RequestModel
{
[FromRoute, DisplayName("entity"), Required, MaximumLength(255)]
public string Entity { get; set; }
[FromBody, DisplayName("body"), Required]
public BodyModel Body { get; set; }
}
public sealed class BodyModel
{
[DisplayName("latestVersion"), Required, MaximumLength(255)]
public string LatestVersion { get; set; }
[DisplayName("state"), Required]
public ModelState State { get; set; }
}
and the request body being sent is:
{
}
Upvotes: 2
Views: 440
Reputation: 116731
Assuming that the validation errors are serialized as some sort of IDictionary<string, T>
for some T
, then the JSON property names corresponding to the dictionary keys can be piecewise camel-cased between each .
character by creating a custom naming strategy.
Json.NET encapsulates the logic to algorithmically remap property names and dictionary keys (e.g. to camel case) in the NamingStrategy
type, specifically CamelCaseNamingStrategy
. To modify the logic of a naming strategy to apply to each portion of a property name between .
characters, you can adopt the decorator pattern and create a decorator naming strategy that applies some inner strategy to each portion of the name like so:
public class PiecewiseNamingStrategy : NamingStrategy
{
readonly NamingStrategy baseStrategy;
public PiecewiseNamingStrategy(NamingStrategy baseStrategy)
{
if (baseStrategy == null)
throw new ArgumentNullException();
this.baseStrategy = baseStrategy;
}
protected override string ResolvePropertyName(string name)
{
return String.Join(".", name.Split('.').Select(n => baseStrategy.GetPropertyName(n, false)));
}
}
Then, configure MVC as follows:
options.ContractResolver = new DefaultContractResolver
{
NamingStrategy = new PiecewiseNamingStrategy(new CamelCaseNamingStrategy())
{
OverrideSpecifiedNames = true, ProcessDictionaryKeys = true
},
};
This takes advantage of the fact that, as shown in the reference source, CamelCasePropertyNamesContractResolver
is basically just a subclass of DefaultContractResolver
that uses a CamelCaseNamingStrategy
with ProcessDictionaryKeys = true
and OverrideSpecifiedNames = true
.
Notes:
Naming strategies were introduces in Json.NET 9.0.1 so this answer does not apply to earlier versions.
You may want to cache the contract resolver statically for best performance.
By setting NamingStrategy.ProcessDictionaryKeys
to true
, the naming strategy will be applied to all dictionary keys.
Upvotes: 3