Sunday, 2 October 2016

.NET Core CSV Writer

Let's discuss how to implementation of generic CSV Writer, which may take input any List and return a CSV string or write to specified file if specified. Although, this is a generic C# implementation and can be used in any .NET Framework supporting generics, yet we are going to discuss this with .NET Core. We are going to use .NET Core Console Application from previous discussion Welcome to .NET Core Console Application.

Add new Class in DotNetCore.ConsoleApplication

We are going to add a new class CsvWriter in DotNetCore.ConsoleApplication.
  • Open existing Solution in Visual Studio 2015.
  • Now add a new class CsvWriter.cs.
    • Open Add New Item Screen through DotNetCore.ConsoleApplication Context Menu of Common folder >> Add >> Class >> Installed >> .NET Core >> Class.
    • Name it CsvWriter.cs.
    • Click OK Button. 
  • Add CsvWriter implementation.
    • Write<T> (IList<T> list, bool includeHeader = true)
    • Creates and returns generated CSV. 
    • Write<T> (IList<T> list, string fileName, bool includeHeader = true)
      Creates and returns generated CSV and saves the generated CSV to specified path.
    • CreateCsvHeaderLine
      Creates CSV header line if includeHeader is sent true.
    • CreateCsvLine<T>(T item, PropertyInfo[] properties)
      Creates a CSV line for given type of object.
    • CreateCsvLine(IList<string> list)
      Creates a CSV line for given list of string by joining them delimated by comma.
    • CreateCsvItem
      Adds provided value item to processed list used to create CSV line.
    • CreateCsvStringListItem
      Adds provided string list as single item to processed list used to create CSV line.
    • CreateCsvStringArrayItem
      Adds provided string array as single item to processed list used to create CSV line.
    • CreateCsvStringItem
      Adds provided string value item to processed list used to create CSV line.
    • ProcessStringEscapeSequence
      Processes the provided data to handle double qoutes and comma value. If we do not apply escape sequences then they can croupt data.
    • WriteFile
      Writes the generated CSV data to file.

 public class CsvWriter
 {
    private const string DELIMITER = ",";

    public string Write<T>(IList<T> list, bool includeHeader = true)
    {
        StringBuilder sb = new StringBuilder();

        Type type = typeof(T);

        PropertyInfo[] properties = type.GetProperties();

        if (includeHeader)
        {
            sb.AppendLine(this.CreateCsvHeaderLine(properties));
        }

        foreach (var item in list)
        {
            sb.AppendLine(this.CreateCsvLine(item, properties));
        }

        return sb.ToString();
    }

    public string Write<T>(IList<T> list, string fileName, bool includeHeader = true)
    {
        string csv = this.Write(list, includeHeader);

        this.WriteFile(fileName, csv);

        return csv;
    }

    private string CreateCsvHeaderLine(PropertyInfo[] properties)
    {
        List<string> propertyValues = new List<string>();

        foreach (var prop in properties)
        {
            string formatString = string.Empty;
            string value = prop.Name;

            var attribute = prop.GetCustomAttribute(typeof(DisplayAttribute));
            if (attribute != null)
            {
                value = (attribute as DisplayAttribute).Name;
            }

            this.CreateCsvStringItem(propertyValues, value);
        }

        return this.CreateCsvLine(propertyValues);
    }

    private string CreateCsvLine<T>(T item, PropertyInfo[] properties)
    {
        List<string> propertyValues = new List<string>();

        foreach (var prop in properties)
        {
            string formatString = string.Empty;
            object value = prop.GetValue(item, null);

            if (prop.PropertyType == typeof(string))
            {
                this.CreateCsvStringItem(propertyValues, value);
            }
            else if (prop.PropertyType == typeof(string[]))
            {
                this.CreateCsvStringArrayItem(propertyValues, value);
            }
            else if (prop.PropertyType == typeof(List<string>))
            {
                this.CreateCsvStringListItem(propertyValues, value);
            }
            else
            {
                this.CreateCsvItem(propertyValues, value);
            }
        }

        return this.CreateCsvLine(propertyValues);
    }

    private string CreateCsvLine(IList<string> list)
    {
        return string.Join(CsvWriter.DELIMITER, list);
    }

    private void CreateCsvItem(List<string> propertyValues, object value)
    {
        if (value != null)
        {
            propertyValues.Add(value.ToString());
        }
        else
        {
            propertyValues.Add(string.Empty);
        }
    }

    private void CreateCsvStringListItem(List<string> propertyValues, object value)
    {
        string formatString = "\"{0}\"";
        if (value != null)
        {
            value = this.CreateCsvLine((List<string>)value);
            propertyValues.Add(string.Format(formatString, this.ProcessStringEscapeSequence(value)));
        }
        else
        {
            propertyValues.Add(string.Empty);
        }
    }

    private void CreateCsvStringArrayItem(List<string> propertyValues, object value)
    {
        string formatString = "\"{0}\"";
        if (value != null)
        {
            value = this.CreateCsvLine(((string[])value).ToList());
            propertyValues.Add(string.Format(formatString, this.ProcessStringEscapeSequence(value)));
        }
        else
        {
            propertyValues.Add(string.Empty);
        }
    }

    private void CreateCsvStringItem(List<string> propertyValues, object value)
    {
        string formatString = "\"{0}\"";
        if (value != null)
        {
            propertyValues.Add(string.Format(formatString, this.ProcessStringEscapeSequence(value)));
        }
        else
        {
            propertyValues.Add(string.Empty);
        }
    }

    private string ProcessStringEscapeSequence(object value)
    {
        return value.ToString().Replace("\"", "\"\"");
    }

    public bool WriteFile(string fileName, string csv)
    {
        bool fileCreated = false;

        if (!string.IsNullOrWhiteSpace(fileName))
        {
            File.WriteAllText(fileName, csv);

            fileCreated = true;
        }

        return fileCreated;
    }
 }

Add Test Model Class in DotNetCore.ConsoleApplication

We are going to add a new class TestVM in DotNetCore.ConsoleApplication.
  • Open existing Solution in Visual Studio 2015.
  • Now add a new class TestVM.cs.
    • Open Add New Item Screen through DotNetCore.ConsoleApplication Context Menu of Common folder >> Add >> Class >> Installed >> .NET Core >> Class.
    • Name it TestVM.cs.
    • Click OK Button. 
  • Add TestVM implementation.
  • Update Program.cs to initialize List<TestVM> with dummy data and call CsvWriter.



 public class TestVM  
 {  
   [Display(Name = "Test Id")]  
   public int TestId { get; set; }  
   [Display(Name = "Name")]  
   public string TestName { get; set; }  
 }  

 public class Program  
 {  
   public static void Main(string[] args)  
   {  
     Console.WriteLine("Welcome to .NET Core Console Application");  
     List<TestVM> tests = new List<TestVM>  
     {  
       new TestVM {TestId=1, TestName="Bill Gates" },  
       new TestVM {TestId=2, TestName="Warren Buffett" },  
       new TestVM {TestId=3, TestName="Amancio Ortega" },  
       new TestVM {TestId=4, TestName="Carlos Slim Helu" }  
     };  
     string fileName = string.Format("{0}\\test.csv", System.AppContext.BaseDirectory);  
     CsvWriter csvWriter = new CsvWriter();  
     csvWriter.Write(tests, fileName, true);  
     Console.WriteLine("{0} has been created.", fileName);  
     Console.ReadKey();  
   }  
 }  

Run Application in Debug Mode

  • Press F5 or Debug Menu >> Start Debugging or Start Console Application Button on Toolbar to start application in debugging mode. It will start application console in debug mode.
  • It will generate test.csv at given path. Therefore at C:\ASP.NET Core\CSV Writer\DotNetCore\ConsoleApplication.NetCore\bin\Debug\netcoreapp1.0.


Sample Source Code

We have placed sample code for this session in ".NET Core CSV Writer_Code.zip" in https://aspdotnetcore.codeplex.com/SourceControl/latest CodePlex repository.

No comments:

Post a Comment