【问题标题】:Use attributes to make headers more human readable with CSVHelper通过 CSVHelper 使用属性使标题更易于阅读
【发布时间】:2018-01-29 20:40:03
【问题描述】:

我正在尝试使用 CSVHelper 序列化由多个类构成的数据库,如下所示。我想通过添加有关单位的信息(适当时)和对数据进行排序以使“名称”始终首先出现,从而使 csv 更具人类可读性。其余的可以按任何顺序出现。

我有一个如下所示的课程。

[DataContract(IsReference = true)]
public class OpaqueMaterial : LibraryComponent
{
    [DataMember]
    [Units("W/m.K")]
    public double Conductivity { get; set; } = 2.4;

    [DataMember]
    public string Roughness { get; set; } = "Rough";
}
[DataContract]
public abstract class LibraryComponent
{
     [DataMember, DefaultValue("No name")]
     public string Name { get; set; } = "No name";
}

为了避免为每个类编写单独的读写函数,我使用如下模板函数进行读写:

public void writeLibCSV<T>(string fp, List<T> records)
{
    using (var sw = new StreamWriter(fp))
    {
        var csv = new CsvWriter(sw);
        csv.WriteRecords(records);
    }
}
public List<T> readLibCSV<T>(string fp)
{
    var records = new List<T>();
    using (var sr = new StreamReader(fp))
    {
        var csv = new CsvReader(sr);
        records = csv.GetRecords<T>().ToList();
    }
    return records;
}

然后我在代码中使用它来读写:

writeLibCSV<OpaqueMaterial>(folderPath + @"\OpaqueMaterial.csv", lib.OpaqueMaterial.ToList());
List<OpaqueMaterial> inOpaqueMaterial = readLibCSV<OpaqueMaterial>(folderPath + @"\OpaqueMaterial.csv");

CSV 输出如下所示:

Conductivity, Roughnes, Name
2.4, Rough, No Name

我想成为:

Name, Conductivity [W/m.K], Roughness
No Name, 2.4, Rough

我知道可以使用以下地图重新排序:

public class MyClassMap : ClassMap<OpaqueMaterial>
{
    public MyClassMap()
    {
        Map(m => m.Name).Index(0);
        AutoMap();
    }
}

我想把这个抽象化,这样我就不必对每个类应用不同的映射。我找不到可以帮助添加自定义标题的示例。任何建议或帮助将不胜感激。

【问题讨论】:

  • CsvHelper doesn't directly support 以这种方式使用属性。如果能够做到这一点而不是使用提供的流畅映射 API 对您来说非常重要,您可以编写自己的 CustomAttribute 类型,并编写代码使用反射来发现这些属性并根据它找到的内容映射属性。跨度>

标签: c# attributes csvhelper


【解决方案1】:

您可以创建ClassMap&lt;T&gt; 的通用版本,它将使用反射自动检查类型T,然后根据它找到的属性以及可能附加或可能不附加到它的属性动态构建映射.

在不太了解 CsvHelper 库的情况下,这样的事情应该可以工作:

public class AutoMap<T> : ClassMap<T>
{
    public AutoMap()
    {
        var properties = typeof(T).GetProperties();

        // map the name property first
        var nameProperty = properties.FirstOrDefault(p => p.Name == "Name");
        if (nameProperty != null)
            MapProperty(nameProperty).Index(0);

        foreach (var prop in properties.Where(p => p != nameProperty))
            MapProperty(prop);
    }

    private MemberMap MapProperty(PropertyInfo pi)
    {
        var map = Map(typeof(T), pi);

        // set name
        string name = pi.Name;
        var unitsAttribute = pi.GetCustomAttribute<UnitsAttribute>();
        if (unitsAttribute != null)
            name = $"{name} {unitsAttribute.Unit}";
        map.Name(name);

        // set default
        var defaultValueAttribute = pi.GetCustomAttribute<DefaultValueAttribute>();
        if (defaultValueAttribute != null)
            map.Default(defaultValueAttribute.Value);

        return map;
    }
}

现在,您只需为要支持的每种 T 类型创建一个 AutoMap&lt;T&gt;

我添加了 UnitsAttributeDefaultValueAttribute 的示例,如果您需要更多属性,应该可以让您了解如何处理更多属性。

【讨论】:

  • 嗨戳。非常感谢你。像魅力一样工作。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-29
  • 1970-01-01
  • 2019-04-15
  • 2011-04-17
  • 1970-01-01
相关资源
最近更新 更多