【问题标题】:Generic class to CSV (all properties)CSV 的通用类(所有属性)
【发布时间】:2011-03-22 16:38:28
【问题描述】:

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

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

some1 能否指导我找到可能的解决方案(如果已经回答)。

谢谢!

【问题讨论】:

  • 所以.. 就写吧?这是一个序列化过程,但不是 XML 或 Binary,而是 CSV。你有什么问题?
  • 我无法想象这会以一种明智的方式实现。如果您有一个对象,其中包含一个字典,其中包含包含其他集合的对象等,您将如何将其存储在 CSV 中。虽然 XML 可能是可能的,但如果多个对象引用相同的数据,可能会变得非常混乱。
  • 给出 (a) 一个类的例子,和 (b) 一个你期望的 CSV 例子,你会得到很多关于如何从 (a) 到 (b) 的答案。
  • silky:我可以写,但我需要一些通用的东西,因为当我在类上添加属性时,我不必担心 CSV .. Doc:简单的数据类,如 User(name,surname,年龄...)。我在想像“;”这样的东西分隔,包括初学者的标题。
  • 是的,我知道这一点。但我会在简单的类上使用 ToCSV ...

标签: c# csv


【解决方案1】:

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

public static class CsvSerializer {
    /// <summary>
    /// 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
    /// </summary>
    public static void Serialize<T>(TextWriter output, IEnumerable<T> 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<string> FormatObject<T>(IEnumerable<MemberInfo> 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<string> 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; }
    }
}

【讨论】:

    【解决方案2】:

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

    Best practices for serializing objects to a custom string format for use in an output file

    这对你有用吗?

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

    【讨论】:

    • 看起来不错,我从未使用过扩展方法:/ 我创建了一个静态类,并添加了 Per Hejndorf 发布在链接的 ost 中的方法。现在我不知道如何将方法扩展到 List,所以我可以在包含“用户”类实例的列表实例上调用 .ToCSV ...
    【解决方案3】:

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

    【讨论】:

      【解决方案4】:

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

      public string ExportCsv<T>(IEnumerable<T> items, Dictionary<string, string> headers)
      {
          string result;
          using (TextWriter textWriter = new StreamWriter(myStream, myEncoding))
          {
              result = this.WriteDataAsCsvWriter<T>(items, textWriter, headers);
          }
          return result;
      }
      
      private string WriteDataAsCsvWriter<T>(IEnumerable<T> items, TextWriter textWriter, Dictionary<string, string> headers)
      {
          //Add null validation
      
          ////print the columns headers
          StringBuilder sb = new StringBuilder();
      
          //Headers
          foreach (KeyValuePair<string, string> 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<string, string> 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;
      }
      

      这是最重要的功能,它从(几乎)任何通用类中提取属性值。

      private Dictionary<string, string> GetPropertiesValues(object item, Dictionary<string, string> headers)
      {
          Dictionary<string, string> values = new Dictionary<string, string>();
          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<string, string> 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<string, string> headers = new Dictionary<string, string>();
      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"}}
      

      【讨论】:

        【解决方案5】:

        我的回答是基于上面 Michael Kropat 的回答。

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

            public static string ToCsvString<T>(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>(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));
            }
        

        【讨论】:

          猜你喜欢
          • 2018-10-04
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-10-20
          • 1970-01-01
          相关资源
          最近更新 更多