2010-07-29 17 views
12

Estoy buscando una forma de crear CSV desde todas las instancias de clase.Clase genérica a CSV (todas las propiedades)

Lo que quiero es que pueda exportar CUALQUIER clase (todas sus instancias) a CSV.

Puede alguien1 dirigirme a la posible solución para esto (en caso de que ya lo haya hecho).

gracias!

+0

Así que .. solo escribirlo? Es un proceso de serialización, pero en lugar de XML o Binario es a CSV. ¿Qué problema estás teniendo? –

+0

Proporcione (a) un ejemplo para una clase, y (b) un ejemplo para el archivo CSV que espera, y obtendrá muchas respuestas sobre cómo ir de (a) a (b). –

+0

sedoso: podría escribirlo, pero necesito algo genérico, ya que cuando agregue propiedades en las clases no tendré que preocuparme por CSV .. Doc: clases de datos simples como Usuario (nombre, apellido, edad ...) . Estaba pensando algo así como ";" delimitado incluyendo encabezado para principiantes. – no9

Respuesta

6

En realidad, algo similar se ha abordado aquí:

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

¿Es esta útil para usted?

Hay un ejemplo que usa la reflexión para extraer los nombres y valores de campo y anexarlos a una cadena.

+0

se ve bien, nunca usé métodos de extensión:/ Creé una clase estática y agregué los métodos Per Hejndorf publicados en el enlace ost u. Ahora no sé cómo extender los métodos a List <>, así que podría llamar a .ToCSV en una instancia de la lista que contenga instancias de digamos clase "Usuario" ... – no9

1

Puede usar reflection para recorrer todas las propiedades/campos de clase y escribirlos en CSV. Un mejor enfoque sería definir un atributo personalizado y decorar los miembros que desea exportar y solo exportar esos atributos.

6

Echa un vistazo a LINQ to CSV. Aunque es un poco pesado, es por eso que escribí el siguiente código para realizar solo el pequeño subconjunto de funcionalidades que necesitaba. Maneja las propiedades y los campos, como usted solicitó, aunque no mucho más. Una cosa que hace es escaparse correctamente de la salida en caso de que contenga comas, comillas o caracteres de nueva línea.

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; } 
    } 
} 
0

Me estoy separando mi respuesta en dos secciones: El primero de ellos es cómo exportar alguna lista de elementos genéricos en csv, la codificación, encabezados - (que construirá datos CSV sólo para los encabezados especificados, e ignorará propiedades innecesarias).

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; 
} 

Esta es la función más importante, es la extracción de los valores de propiedades de (casi) cualquier clase genérica.

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; 
} 

Ejemplo para 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"}} 
Cuestiones relacionadas