C# .NET Core 2.2如何反序列化含动态列列表的API响应?
嘿,这个动态列名的反序列化问题我之前也踩过坑!手动按索引映射不仅麻烦,接口一改就炸,确实闹心。给你几个更优雅高效的方案,看看哪个适合你的场景:
方案1:利用Newtonsoft.Json的字典转对象能力(最快捷)
这个方案不用写复杂的反射逻辑,直接借助成熟的JSON库帮你完成映射,代码简洁还不容易出错。
首先定义对应接口响应的基础类和你的目标Model:
public class ApiResponse { public List<string> ColumnNames { get; set; } public List<List<object>> Data { get; set; } } public class MyModel { public DateTime Date { get; set; } public decimal Value { get; set; } public decimal SomeOtherValue { get; set; } }
然后处理反序列化和映射:
// 先把接口响应反序列化成基础ApiResponse var apiResponse = await response.Content.ReadAsAsync<ApiResponse>(); var models = new List<MyModel>(); foreach (var row in apiResponse.Data) { // 用Zip把列名和行数据一一配对生成字典 var rowDictionary = apiResponse.ColumnNames .Zip(row, (columnName, cellValue) => new { columnName, cellValue }) .ToDictionary(pair => pair.columnName, pair => pair.cellValue); // 把字典序列化成JSON再反序列化成目标Model var model = JsonConvert.DeserializeObject<MyModel>( JsonConvert.SerializeObject(rowDictionary) ); models.Add(model); }
为什么这个方案更好?
- 不用手动维护索引匹配,完全靠列名和Model属性名对应(Newtonsoft默认大小写不敏感)
- 接口新增列时,只要Model里没有对应属性,不会报错(如果想严格控制,可以配置序列化选项)
- 类型转换由库自动处理,不用自己写Convert逻辑
方案2:System.Text.Json版本(适合.NET Core/.NET 5+)
如果你用的是官方的System.Text.Json(没有引入Newtonsoft),思路和上面一致,只是API略有不同:
// 反序列化接口响应 var stream = await response.Content.ReadAsStreamAsync(); var apiResponse = await JsonSerializer.DeserializeAsync<ApiResponse>(stream); var models = new List<MyModel>(); var serializeOptions = new JsonSerializerOptions { IgnoreUnknownFields = true }; foreach (var row in apiResponse.Data) { var rowDictionary = apiResponse.ColumnNames .Zip(row, (col, val) => KeyValuePair.Create(col, val)) .ToDictionary(kv => kv.Key, kv => kv.Value); // 字典转JSON再转Model var jsonString = JsonSerializer.Serialize(rowDictionary); var model = JsonSerializer.Deserialize<MyModel>(jsonString, serializeOptions); models.Add(model); }
这里加了IgnoreUnknownFields = true,确保接口新增列时不会因为Model没有对应属性而抛出异常,容错性更强。
方案3:AutoMapper自定义转换器(适合大数据量场景)
如果你的数据量很大,序列化两次(字典→JSON→Model)可能有点性能损耗,或者项目已经在用AutoMapper,可以用自定义转换器直接完成映射:
首先定义转换器:
public class RowToModelConverter : ITypeConverter<List<object>, MyModel> { private readonly List<string> _columnNames; public RowToModelConverter(List<string> columnNames) { _columnNames = columnNames; } public MyModel Convert(List<object> source, MyModel destination, ResolutionContext context) { var model = new MyModel(); var modelProperties = typeof(MyModel).GetProperties(); foreach (var prop in modelProperties) { // 按列名找对应索引(忽略大小写) var columnIndex = _columnNames.IndexOf(prop.Name, StringComparison.OrdinalIgnoreCase); if (columnIndex >= 0 && columnIndex < source.Count) { // 自动转换类型并赋值 var convertedValue = Convert.ChangeType(source[columnIndex], prop.PropertyType); prop.SetValue(model, convertedValue); } } return model; } }
然后使用转换器完成映射:
var mapperConfig = new MapperConfiguration(cfg => { cfg.CreateMap<List<object>, MyModel>() .ConvertUsing(new RowToModelConverter(apiResponse.ColumnNames)); }); var mapper = mapperConfig.CreateMapper(); var models = apiResponse.Data.Select(row => mapper.Map<MyModel>(row)).ToList();
这个方案直接通过反射一次完成映射,避免了中间的JSON序列化步骤,性能会更好一些。
总结
- 如果你只是想快速解决问题,方案1或2最省心,不用额外依赖,代码量少
- 如果处理的数据量很大,或者已经在用AutoMapper,方案3更高效
- 这三个方案都比手动索引映射更健壮,接口列名顺序变更、新增列都不会轻易失效
内容的提问来源于stack exchange,提问作者qubits




