CSV的通用类(所有属性)

我正在寻找一种从所有类实例创建CSV的方法。

我想要的是我可以将任何类(其所有实例)导出为CSV。

some1可以指导我为此提供可能的解决方案(如果已经被证实的话)。

thanx!

实际上,这里已经解决了类似的问题:

将对象序列化为自定义字符串格式以在输出文件中使用的最佳实践

这对你有用吗?

有一个示例使用reflection来提取字段名称和值,并将它们附加到字符串。

看一下LINQ to CSV 。 虽然它有点偏重,这就是为什么我编写以下代码来执行我需要的一小部分function。 它处理属性和字段,就像你要求的那样,尽管不是很多。 它做的一件事是正确地转义输出,以防它包含逗号,引号或换行符。

public static class CsvSerializer { ///  /// Serialize objects to Comma Separated Value (CSV) format [1]. /// /// Rather than try to serialize arbitrarily complex types with this /// function, it is better, given type A, to specify a new type, A'. /// Have the constructor of A' accept an object of type A, then assign /// the relevant values to appropriately named fields or properties on /// the A' object. /// /// [1] http://tools.ietf.org/html/rfc4180 ///  public static void Serialize(TextWriter output, IEnumerable objects) { var fields = from mi in typeof (T).GetMembers(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static) where new [] { MemberTypes.Field, MemberTypes.Property }.Contains(mi.MemberType) let orderAttr = (ColumnOrderAttribute) Attribute.GetCustomAttribute(mi, typeof (ColumnOrderAttribute)) orderby orderAttr == null ? int.MaxValue : orderAttr.Order, mi.Name select mi; output.WriteLine(QuoteRecord(fields.Select(f => f.Name))); foreach (var record in objects) { output.WriteLine(QuoteRecord(FormatObject(fields, record))); } } static IEnumerable FormatObject(IEnumerable fields, T record) { foreach (var field in fields) { if (field is FieldInfo) { var fi = (FieldInfo) field; yield return Convert.ToString(fi.GetValue(record)); } else if (field is PropertyInfo) { var pi = (PropertyInfo) field; yield return Convert.ToString(pi.GetValue(record, null)); } else { throw new Exception("Unhandled case."); } } } const string CsvSeparator = ","; static string QuoteRecord(IEnumerable record) { return String.Join(CsvSeparator, record.Select(field => QuoteField(field)).ToArray()); } static string QuoteField(string field) { if (String.IsNullOrEmpty(field)) { return "\"\""; } else if (field.Contains(CsvSeparator) || field.Contains("\"") || field.Contains("\r") || field.Contains("\n")) { return String.Format("\"{0}\"", field.Replace("\"", "\"\"")); } else { return field; } } [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] public class ColumnOrderAttribute : Attribute { public int Order { get; private set; } public ColumnOrderAttribute(int order) { Order = order; } } } 

您可以使用reflection遍历所有类属性/字段并将其写入CSV。 更好的方法是定义自定义属性并装饰要导出的成员,并仅导出这些属性。

我将我的答案分成两部分:第一部分是如何将一些通用项目列表导出到csv,带有编码,标题 – (它将仅为指定的标题构建csv数据,并将忽略不需要的属性)。

 public string ExportCsv(IEnumerable items, Dictionary headers) { string result; using (TextWriter textWriter = new StreamWriter(myStream, myEncoding)) { result = this.WriteDataAsCsvWriter(items, textWriter, headers); } return result; } private string WriteDataAsCsvWriter(IEnumerable items, TextWriter textWriter, Dictionary headers) { //Add null validation ////print the columns headers StringBuilder sb = new StringBuilder(); //Headers foreach (KeyValuePair kvp in headers) { sb.Append(ToCsv(kvp.Value)); sb.Append(","); } sb.Remove(sb.Length - 1, 1);//the last ',' sb.Append(Environment.NewLine); //the values foreach (var item in items) { try { Dictionary values = GetPropertiesValues(item, headers); foreach (var value in values) { sb.Append(ToCsv(value.Value)); sb.Append(","); } sb.Remove(sb.Length - 1, 1);//the last ',' sb.Append(Environment.NewLine); } catch (Exception e1) { //do something } } textWriter.Write(sb.ToString()); return sb.ToString(); } //Help function that encode text to csv: public static string ToCsv(string input) { if (input != null) { input = input.Replace("\r\n", string.Empty) .Replace("\r", string.Empty) .Replace("\n", string.Empty); if (input.Contains("\"")) { input = input.Replace("\"", "\"\""); } input = "\"" + input + "\""; } return input; } 

这是最重要的函数,它从(几乎)任何generics类中提取属性值。

 private Dictionary GetPropertiesValues(object item, Dictionary headers) { Dictionary values = new Dictionary(); if (item == null) { return values; } //We need to make sure each value is coordinated with the headers, empty string foreach (var key in headers.Keys) { values[key] = String.Empty; } Type t = item.GetType(); PropertyInfo[] propertiesInfo = t.GetProperties(); foreach (PropertyInfo propertiyInfo in propertiesInfo) { //it not complex: string, int, bool, Enum if ((propertiyInfo.PropertyType.Module.ScopeName == "CommonLanguageRuntimeLibrary") || propertiyInfo.PropertyType.IsEnum) { if (headers.ContainsKey(propertiyInfo.Name)) { var value = propertiyInfo.GetValue(item, null); if (value != null) { values[propertiyInfo.Name] = value.ToString(); } } } else//It's complex property { if (propertiyInfo.GetIndexParameters().Length == 0) { Dictionary lst = GetPropertiesValues(propertiyInfo.GetValue(item, null), headers); foreach (var value in lst) { if (!string.IsNullOrEmpty(value.Value)) { values[value.Key] = value.Value; } } } } } return values; } 

GetPropertiesValues示例:

 public MyClass { public string Name {get; set;} public MyEnum Type {get; set;} public MyClass2 Child {get; set;} } public MyClass2 { public int Age {get; set;} public DateTime MyDate {get; set;} } MyClass myClass = new MyClass() { Name = "Bruce", Type = MyEnum.Sometype, Child = new MyClass2() { Age = 18, MyDate = DateTime.Now() } }; Dictionary headers = new Dictionary(); headers.Add("Name", "CustomCaption_Name"); headers.Add("Type", "CustomCaption_Type"); headers.Add("Age", "CustomCaption_Age"); GetPropertiesValues(myClass, headers)); // OUTPUT: {{"Name","Bruce"},{"Type","Sometype"},{"Age","18"}} 

我的回答是基于Michael Kropat的答案。

我在他的答案中添加了两个函数,因为它不想直接写入文件,因为我还有一些进一步的处理要做。 相反,我希望标题信息与值分开,以便我可以在以后将所有内容重新组合在一起。

  public static string ToCsvString(T obj) { var fields = from mi in typeof(T).GetMembers(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static) where new[] { MemberTypes.Field, MemberTypes.Property }.Contains(mi.MemberType) let orderAttr = (ColumnOrderAttribute)Attribute.GetCustomAttribute(mi, typeof(ColumnOrderAttribute)) select mi; return QuoteRecord(FormatObject(fields, obj)); } public static string GetCsvHeader(T obj) { var fields = from mi in typeof(T).GetMembers(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static) where new[] { MemberTypes.Field, MemberTypes.Property }.Contains(mi.MemberType) let orderAttr = (ColumnOrderAttribute)Attribute.GetCustomAttribute(mi, typeof(ColumnOrderAttribute)) select mi; return QuoteRecord(fields.Select(f => f.Name)); }