Well, you should be able to do it using a custom JsonConverter to read your data. Using the deserialization provided in Manojs' answer, you could create a DefaultContractResolver that would create a custom deserialization when the class has a SnakeCasedAttribute specified above.
The ContractResolver would look like the following
public class SnakeCaseContractResolver : DefaultContractResolver { public new static readonly SnakeCaseContractResolver Instance = new SnakeCaseContractResolver(); protected override JsonContract CreateContract(Type objectType) { JsonContract contract = base.CreateContract(objectType); if (objectType?.GetCustomAttributes(true).OfType<SnakeCasedAttribute>().Any() == true) { contract.Converter = new SnakeCaseConverter(); } return contract; } }
The SnakeCaseConverter would be something like this?
public class SnakeCaseConverter : JsonConverter { public override bool CanConvert(Type objectType) => objectType.GetCustomAttributes(true).OfType<SnakeCasedAttribute>().Any() == true; private static string ConvertFromSnakeCase(string snakeCased) { return string.Join("", snakeCased.Split('_').Select(part => part.Substring(0, 1).ToUpper() + part.Substring(1))); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { var target = Activator.CreateInstance( objectType ); var jobject = JObject.Load(reader); foreach (var property in jobject.Properties()) { var propName = ConvertFromSnakeCase(property.Name); var prop = objectType.GetProperty(propName); if (prop == null || !prop.CanWrite) { continue; } prop.SetValue(target, property.Value.ToObject(prop.PropertyType, serializer)); } return target; } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { throw new NotImplementedException(); } }
And then you could annotate your dto class using this attribute (which is just a placeholder)
[SnakeCased] public class InputObjectDTO { public string FullName { get; set; } public int TotalPrice { get; set; } }
and for reference, this is the used attribute
[AttributeUsage(AttributeTargets.Class)] public class SnakeCasedAttribute : Attribute { public SnakeCasedAttribute() { // intended blank } }
One more thing to notice is that in your current form the JSON converter would throw an error ("20.00" is not an int), but I am going to guess that from here you can handle that part yourself :)
And for a complete reference, you could see the working version in this dotnetfiddle