You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用CsvHelper导出指定列并保留属性定义的配置?

问题

现有一个通过特性定义配置的类SomeClass,以及一个可导出指定字段到CSV的方法GetAsCsvByteArray<T>。当传入的fields参数为null时,导出格式正确;但传入指定属性名数组(如["SomeInteger", "SomeBoolean"])时,属性上的特性配置丢失,导出格式异常。请问如何保留属性定义的配置?

相关代码

定义配置的类

public class SomeClass
{
    public int SomeInteger { get; set; }
    public string SomeString { get; set; }
    public float SomeFloat { get; set; }

    [BooleanFalseValues("No")]
    [BooleanTrueValues("Yes")]
    public bool SomeBoolean { get; set; }

    [Format(@"#\%")]
    public decimal SomeDecimal { get; set; }

    [Format("dd-MM-yyyy HH:mm:ss")]
    public DateTime SomeDateTime { get; set; }
}

导出方法

public static byte[] GetAsCsvByteArray<T>(IEnumerable<T> data, string[] fields)
{
    var csvConfig = new CsvConfiguration(CultureInfo.InvariantCulture)
    {
        Delimiter = ";",
        ShouldQuote = _ => true
    };

    using var memoryStream = new MemoryStream();
    using var writer = new StreamWriter(memoryStream);
    using var csv = new CsvWriter(writer, csvConfig);

    if (fields?.Length > 0)
    {
        var csvMap = new DefaultClassMap<T>();

        foreach (var field in fields)
        {
            var property = typeof(T).GetProperty(field);
            csvMap.Map(typeof(T), property);
        }

        csv.Context.RegisterClassMap(csvMap);
    }

    csv.WriteRecords(data);

    return memoryStream.ToArray();
}
解决方案

问题出在手动创建DefaultClassMap<T>并逐个添加属性映射的逻辑上——这种方式下CsvHelper不会自动读取属性上的特性配置,导致格式规则丢失。以下是两种可行的解决方式:

方案一:自动映射后筛选指定字段(推荐)

先让CsvHelper自动生成包含所有特性配置的映射规则,再移除不需要的字段,只保留指定的fields:

public static byte[] GetAsCsvByteArray<T>(IEnumerable<T> data, string[] fields)
{
    var csvConfig = new CsvConfiguration(CultureInfo.InvariantCulture)
    {
        Delimiter = ";",
        ShouldQuote = _ => true
    };

    using var memoryStream = new MemoryStream();
    using var writer = new StreamWriter(memoryStream);
    using var csv = new CsvWriter(writer, csvConfig);

    if (fields?.Length > 0)
    {
        // 自动生成包含所有特性配置的映射
        var csvMap = csv.Context.AutoMap<T>();
        // 移除不在指定字段列表中的映射项
        var unwantedMembers = csvMap.Members.Where(m => !fields.Contains(m.Member.Name)).ToList();
        foreach (var member in unwantedMembers)
        {
            csvMap.Members.Remove(member);
        }
        // 重新注册修改后的映射
        csv.Context.RegisterClassMap(csvMap);
    }

    csv.WriteRecords(data);

    return memoryStream.ToArray();
}

这种方式无需手动处理每种特性,AutoMap会自动识别并应用所有CsvHelper特性,同时能精准保留指定字段。

方案二:手动映射时逐个应用特性

如果需要更精细的控制,可以在手动添加属性映射后,读取属性上的特性并逐个配置到映射规则中:

public static byte[] GetAsCsvByteArray<T>(IEnumerable<T> data, string[] fields)
{
    var csvConfig = new CsvConfiguration(CultureInfo.InvariantCulture)
    {
        Delimiter = ";",
        ShouldQuote = _ => true
    };

    using var memoryStream = new MemoryStream();
    using var writer = new StreamWriter(memoryStream);
    using var csv = new CsvWriter(writer, csvConfig);

    if (fields?.Length > 0)
    {
        var csvMap = new DefaultClassMap<T>();

        foreach (var field in fields)
        {
            var property = typeof(T).GetProperty(field);
            if (property == null) continue;

            var memberMap = csvMap.Map(typeof(T), property);

            // 应用BooleanFalseValues特性
            var falseAttr = property.GetCustomAttribute<BooleanFalseValuesAttribute>();
            if (falseAttr != null)
            {
                memberMap.TypeConverterOption.BooleanFalseValues(falseAttr.Values);
            }

            // 应用BooleanTrueValues特性
            var trueAttr = property.GetCustomAttribute<BooleanTrueValuesAttribute>();
            if (trueAttr != null)
            {
                memberMap.TypeConverterOption.BooleanTrueValues(trueAttr.Values);
            }

            // 应用Format特性
            var formatAttr = property.GetCustomAttribute<FormatAttribute>();
            if (formatAttr != null)
            {
                memberMap.TypeConverterOption.Format(formatAttr.Format);
            }

            // 其他特性可按需添加
        }

        csv.Context.RegisterClassMap(csvMap);
    }

    csv.WriteRecords(data);

    return memoryStream.ToArray();
}

这种方式需要针对每种特性编写处理逻辑,适合需要自定义部分规则的场景。

内容的提问来源于stack exchange,提问作者Niels R.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 21:25:22