Общий класс для CSV (все свойства) - программирование

Общий класс для CSV (все свойства)

Мне нужен способ создания CSV из всех экземпляров класса.

Я хочу, чтобы я мог экспортировать любой класс (все его экземпляры) в CSV.

Может ли some1 направить меня к возможному решению для этого (в случае уже андерсерированного).

thanx!

4b9b3361

Ответ 1

Собственно, здесь было рассмотрено нечто подобное:

Рекомендации по сериализации объектов в пользовательском строчном формате для использования в выходном файле

Это полезно для вас?

Существует образец, который использует отражение, чтобы вытащить имена полей и значения и добавить их в строку.

Ответ 2

Посмотрите 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; }
    }
}

Ответ 3

Вы можете использовать отражение, чтобы пройти все свойства/поля класса и записать их в 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 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"}}