.NET Core WebAPI:带数字后缀的蛇形命名JSON属性无法映射DTO
解决.NET Core WebAPI中带数字后缀的蛇形JSON属性无法绑定到DTO的问题
我在使用集成Swagger的.NET Core WebAPI项目时,遇到了JSON属性绑定异常。当传入如下请求JSON时:
{ "year": 2023, "vat_amount": 2000, "vat_period": "January", "vat_amount_20": 2000, "turnover_description": "Some description of turnover", "turnover_description_10": "Turnover Description 10", "turnover_description_20": "Turnover Description 20", "related_party_identifier": "106723546" }
大部分属性能被Newtonsoft.Json识别并填充到DTO,但带字母数字组合的属性(如vat_amount_20、turnover_description_10、turnover_description_20)无法正确绑定。
我的DTO定义如下:
[JsonObject(NamingStrategyType = typeof(SnakeCaseNamingStrategy))] public class VatRecordingIndividualRequestDto { [JsonProperty("turnover_description")] public string? TurnoverDescription { get; set; } [JsonProperty("turnover_description_10")] public string? TurnoverDescription10 { get; set; } [JsonProperty("turnover_description_20")] public string? TurnoverDescription20 { get; set; } [JsonProperty("vat_amount_20")] public double? VatAmount20 { get; set; } [JsonProperty("vat_amount")] public double? VatAmount { get; set; } [JsonProperty("vat_period")] public string? VatPeriod { get; set; } [JsonProperty("year")] public int Year { get; set; } }
同时在Program.cs中配置了自定义的SnakeCasePropertyNamingPolicy(基于System.Text.Json):
builder.Services.AddControllers().AddJsonOptions(options => { options.JsonSerializerOptions.PropertyNamingPolicy = new SnakeCasePropertyNamingPolicy(); });
自定义命名策略代码:
public class SnakeCasePropertyNamingPolicy : JsonNamingPolicy { public override string ConvertName(string name) { return name.ToSnakeCase(); } } public static class StringExtensions { public static string ToSnakeCase(this string str) { return string.Concat(str.Select((character, index) => index > 0 && char.IsUpper(character) ? "_" + character : character.ToString())) .ToLower(); } }
问题根源
当前的ToSnakeCase扩展方法仅处理了大写字母前加下划线的情况,但未覆盖字母后跟数字的场景。比如DTO属性VatAmount20,按照现有逻辑会被转换为vat_amount20,但请求JSON中的对应属性是vat_amount_20(数字前多了下划线),两者不匹配导致绑定失败。
解决方案
修改ToSnakeCase方法,新增字母后跟数字场景的处理逻辑,在字母与数字之间插入下划线。修改后的代码如下:
public static class StringExtensions { public static string ToSnakeCase(this string str) { var result = new StringBuilder(); for (int i = 0; i < str.Length; i++) { var currentChar = str[i]; // 处理大写字母:前面加下划线后转小写 if (char.IsUpper(currentChar)) { if (i > 0) { result.Append('_'); } result.Append(char.ToLower(currentChar)); } // 处理数字:若前一个字符是字母,先加下划线再追加数字 else if (char.IsDigit(currentChar)) { if (i > 0 && char.IsLetter(str[i - 1])) { result.Append('_'); } result.Append(currentChar); } // 其他字符直接追加 else { result.Append(currentChar); } } return result.ToString(); } }
补充说明
- 新增数字判断逻辑后,
VatAmount20会被正确转换为vat_amount_20,与JSON属性名完全匹配,实现正常绑定。 - 使用
StringBuilder替代原LINQ拼接逻辑,性能更优且可读性更强。 - 若项目同时混用Newtonsoft.Json和System.Text.Json,建议保持一致性:
- 若依赖Newtonsoft.Json(已添加
AddNewtonsoftJson()),可移除自定义System.Text.Json命名策略,直接依赖Newtonsoft原生的SnakeCaseNamingStrategy。 - 若使用System.Text.Json,建议移除DTO上的Newtonsoft特性,改用
[JsonPropertyName]指定属性名,或依赖修改后的自定义命名策略自动转换。
- 若依赖Newtonsoft.Json(已添加
内容的提问来源于stack exchange,提问作者lonelydev101
相关产品推荐
相关产品推荐

