You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

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

火山引擎 最新活动