.NET Core 3.1使用JsonPropertyName处理带索引点属性反序列化问题
问题原因
System.Text.Json 原生的JsonPropertyName特性仅支持完全匹配JSON键名,你当前配置的[JsonPropertyName("user.email")]只能匹配键名恰好为user.email的属性,无法自动识别带[索引]后缀的user.email[0]/user.email[1]这类扁平化键,也不会自动把多个同前缀带索引的键聚合为List集合。
解决方案
分两种场景处理:
方案1:调整前端传参结构(改造成本最低)
如果可以协调前端修改传参格式,直接传标准嵌套JSON结构即可,无需额外开发:
{ "id": "B1346", "businessType": "RENTAL", "company.city": "LUND", "company.zipcode": "99999", "company.email": "support@samplecompany.com", "user": { "email": ["support2@samplecompany.com", "support3@samplecompany.com"], "name": ["Tom", "Harry"] } }
对应修改POCO结构:
public class Form { public string Id { get; set;} public string BusinessType { get; set;} [JsonPropertyName("company.city")] public string CompanyCity { get; set;} [JsonPropertyName("company.zipcode")] public string CompanyZipcode { get; set;} [JsonPropertyName("company.email")] public string CompanyEmail { get; set;} public UserInfo User { get; set; } } public class UserInfo { public List<string> Email { get; set; } public List<string> Name { get; set; } }
默认反序列化即可正常解析。
方案2:自定义JsonConverter(前端传参无法修改时使用)
如果不能调整前端传参格式,手动实现JsonConverter<Form>处理带索引的扁平化键即可:
第一步:实现自定义转换器
using System; using System.Collections.Generic; using System.Text.Json; using System.Text.Json.Serialization; using System.Text.RegularExpressions; public class FormJsonConverter : JsonConverter<Form> { // 匹配带索引的键的正则,比如 user.email[0] 会匹配出前缀user.email、索引0 private readonly Regex _arrayKeyRegex = new Regex(@"^(?<prefix>.+)\[(?<index>\d+)\]$", RegexOptions.Compiled); public override Form Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { var form = new Form { UserEmail = new List<string>(), UserName = new List<string>() }; if (reader.TokenType != JsonTokenType.StartObject) throw new JsonException(); while (reader.Read()) { if (reader.TokenType == JsonTokenType.EndObject) return form; if (reader.TokenType != JsonTokenType.PropertyName) throw new JsonException(); var propertyName = reader.GetString(); reader.Read(); var value = reader.GetString(); // 先处理普通属性 switch (propertyName) { case "id": form.Id = value; continue; case "businessType": form.BusinessType = value; continue; case "company.city": form.CompanyCity = value; continue; case "company.zipcode": form.CompanyZipcode = value; continue; case "company.email": form.CompanyEmail = value; continue; } // 处理带索引的数组属性 var match = _arrayKeyRegex.Match(propertyName); if (!match.Success) continue; var prefix = match.Groups["prefix"].Value; var index = int.Parse(match.Groups["index"].Value); if (prefix == "user.email") { FillList(form.UserEmail, index, value); } else if (prefix == "user.name") { FillList(form.UserName, index, value); } } throw new JsonException(); } // 填充列表,处理索引超过当前列表长度的情况 private void FillList(List<string> list, int index, string value) { while (list.Count <= index) { list.Add(null); } list[index] = value; } public override void Write(Utf8JsonWriter writer, Form value, JsonSerializerOptions options) { // 如果不需要序列化回原扁平化格式可以直接用默认实现,或者自己按需实现 JsonSerializer.Serialize(writer, value, options); } }
第二步:注册转换器
两种注册方式二选一:
- 直接给Form类加特性:
[JsonConverter(typeof(FormJsonConverter))] public class Form { // 原有属性不变,不需要再给UserEmail/UserName加JsonPropertyName特性了 public string Id { get; set;} public string BusinessType { get; set;} [JsonPropertyName("company.city")] public string CompanyCity { get; set;} [JsonPropertyName("company.zipcode")] public string CompanyZipcode { get; set;} [JsonPropertyName("company.email")] public string CompanyEmail { get; set;} public List<string> UserEmail { get; set;} public List<string> UserName { get; set;} } - 全局注册(Startup.cs的ConfigureServices方法中):
services.AddControllers() .AddJsonOptions(options => { options.JsonSerializerOptions.Converters.Add(new FormJsonConverter()); });
内容的提问来源于stack exchange,提问作者Imtiaz
相关产品推荐
相关产品推荐

