如何将对象列表转换为CSV?

19
如果我有一个名为"Car"的对象列表:
public class Car
{
     public string Name;
     public int Year;
     public string Model;
}

如何将对象列表,例如List<Car>转换为csv格式?


你能转换它吗?你试过哪一个? - Praveen
这回答了您的问题吗?[将对象列表快速转换为每个对象值在新行中的csv的最快方法] (https://dev59.com/AF8e5IYBdhLWcg3wfajv) - Jim G.
8个回答

14
  1. FileHelpers
  2. 文本OleDb提供程序
  3. 手动使用字符串拼接按照RFC-4180规定
  4. 第三方库,例如Aspose.Cells可以在不影响你的情况下完成它。而且它的速度非常快。

1
+1为RFC的链接!维基百科上也有一个不错的参考资料:http://en.wikipedia.org/wiki/Comma-separated_values - TrueWill

9
在Car中添加以下方法:
String Escape(String s)
{
    StringBuilder sb = new StringBuilder();
    bool needQuotes = false;
    foreach (char c in s.ToArray())
    {
        switch (c)
        {
            case '"': sb.Append("\\\""); needQuotes = true; break;
            case ' ': sb.Append(" "); needQuotes = true; break;
            case ',': sb.Append(","); needQuotes = true; break;
            case '\t': sb.Append("\\t"); needQuotes = true; break;
            case '\n': sb.Append("\\n"); needQuotes = true; break;
            default: sb.Append(c); break;
        }
    }
    if (needQuotes)
        return "\"" + sb.ToString() + "\"";
    else
        return sb.ToString();
}

public void SerializeAsCsv(Stream stream)
{
    stream.Write(Escape(Name));
    stream.Write(",");
    stream.Write(Year.ToString());
    stream.Write(",");
    stream.Write(Escape(Model));
    stream.Write("\n");
}

现在您可以对整个列表进行序列化:
foreach (Car car in list)
{
    car.SerializeAsCsv(stream);
}

8
请看FileHelpers库。
从网站上可以得知: FileHelpers是一个免费且易于使用的.NET库,可用于在文件、字符串或流中导入/导出固定长度或分隔记录的数据。 这将确保处理行终止、转义等所有可能出现的问题都是正确的,符合RFC-4180标准。

4

我一直在寻找解决方案,但是我找到的答案都没有满足我的简单需求。后来我意识到我的自动CRUD代码已经有了答案。我重新利用它并得出了以下代码:

using System.Reflection;
    /// <summary>
    /// Using a bit of reflection to build up the strings.
    /// </summary>
    public static string ToCsvHeader(this object obj)
    {
        Type type = obj.GetType();
        var properties = type.GetProperties(BindingFlags.DeclaredOnly |
                                       BindingFlags.Public |
                                       BindingFlags.Instance);

        string result = string.Empty;
        Array.ForEach(properties, prop =>
        {
            result += prop.Name + ",";
        });

        return (!string.IsNullOrEmpty(result) ? result.Substring(0, result.Length - 1) : result);
    }

    public static string ToCsvRow(this object obj)
    {
        Type type = obj.GetType();
        var properties = type.GetProperties(BindingFlags.DeclaredOnly |
                                       BindingFlags.Public |
                                       BindingFlags.Instance);

        string result = string.Empty;
        Array.ForEach(properties, prop =>
        {
            var value = prop.GetValue(obj, null);
            var propertyType = prop.PropertyType.FullName;
            if (propertyType == "System.String")
            {
                // wrap value incase of commas
                value = "\"" + value + "\"";
            }

            result += value + ",";

        });

        return (!string.IsNullOrEmpty(result) ? result.Substring(0, result.Length - 1) : result);
    }

这将为每个对象添加一个扩展方法。使用方式如下:
var burgers = new List<Cheeseburger>();
var output = burgers.ToCsvHeader();
output += Environment.NewLine;

burgers.ForEach(burger =>
{
    output += burger.ToCsvRow();
    output += Environment.NewLine;
});
var path = "[where ever you want]";
System.IO.File.WriteAllText(path, output);

也许有更好的方法来编写上述两种方法,但对于我的情况来说,这完美地解决了问题。希望对他人有所帮助。


1
您可以简单地重写ToString方法或创建一个ToCSVRow()方法,如下所示
    public String ToCSVRow()
    {
        return Name + "," + Year.ToString() + "," + Model;
    }

然后只需在需要的地方执行类似这样的操作。

    using (StreamWriter file = new StreamWriter(@"C:\Wherever\yourfilename.txt"))
        {
            foreach (var item in yourlist)
            {
                file.WriteLine(item.ToCSVRow());
            }
        }

0

我会按照这篇文章的建议实现一些额外的序列化行为。如果你想要更高级一点,可以在项目属性中创建一个设置。该设置将决定你的类是否使用csv或默认序列化。然后,你可以通过这里所示的技术来访问它。考虑使用静态构造函数来读取appsettings并使布尔值可访问到你的序列化代码。Vlads'的代码看起来很棒,只需将其插入到你的代码中即可。此外,你还可以考虑其他更理想的方法来改变你的序列化行为。

或者创建一个名为“SerializeAsCSV”的接口,并像这样使用它:

// MyCoolClass.csv的部分内容:

   public class MyCoolClass : ISerializeAsCSV, IDisposable
   {

      protected static bool serializesToCSV = false;


      static MyCoolClass()
      {
         serializesToCSV = 
            (typeof(MyCoolClass).GetInterface("GrooveySoft.Shared.Interfaces.ISerializeAsCSV") == null)
            ? false
            : true;

      }


      public MyCoolClass(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context)
      {
        // your stuff here
      }

      public void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context)
      {
         // your stuff here
      }

    }

// ISerializeAsCSV.cs 的内容

using System.Runtime.Serialization;

namespace GrooveySoft.Shared.Interfaces
{
   /// <summary>
   /// indicates that implementor will serialize to csv format
   /// </summary>
   public interface ISerializeAsCSV : ISerializable
   {
   }
}

这应该能让你开始了……我还没有编译和测试过,但你可以得到大致的想法。


0

组合逗号分隔的值时,我总是喜欢指向被低估的string.Join(string separator, string[] elements)静态方法,但如果有辅助库存在,那可能更好。


0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接