using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.IO; using System.Linq; using System.Reflection; using System.Text; // Ref: https://www.codeproject.com/Articles/566656/CSV-Serializer-for-NET namespace BTCPayServer.Services.Invoices.Export { /// /// Serialize and Deserialize Lists of any object type to CSV. /// #pragma warning disable CA1305 // Specify IFormatProvider public class CsvSerializer where T : class, new() { private List _properties; public bool IgnoreEmptyLines { get; set; } = true; public bool IgnoreReferenceTypesExceptString { get; set; } = true; public string NewlineReplacement { get; set; } = ((char)0x254).ToString(); public char Separator { get; set; } = ','; public string Replacement { get; set; } = ((char)0x255).ToString(); public string RowNumberColumnTitle { get; set; } = "RowNumber"; public bool UseLineNumbers { get; set; } = false; public bool UseTextQualifier { get; set; } = false; public bool UseEofLiteral { get; set; } = false; /// /// Csv Serializer /// Initialize by selected properties from the type to be de/serialized /// public CsvSerializer() { var type = typeof(T); var properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetProperty | BindingFlags.SetProperty); var q = properties.AsQueryable(); if (IgnoreReferenceTypesExceptString) { q = q.Where(a => a.PropertyType.IsValueType || a.PropertyType.Name == "String"); } var r = from a in q where a.GetCustomAttribute() == null select a; _properties = r.ToList(); } /// /// Serialize /// /// stream /// data public string Serialize(IList data) { var sb = new StringBuilder(); var values = new List(); sb.AppendLine(GetHeader()); var row = 1; foreach (var item in data) { values.Clear(); if (UseLineNumbers) { values.Add(row++.ToString()); } foreach (var p in _properties) { var raw = p.GetValue(item); var value = raw == null ? "" : raw.ToString() .Replace(Separator.ToString(), Replacement) .Replace(Environment.NewLine, NewlineReplacement); if (UseTextQualifier) { value = string.Format("\"{0}\"", value); } values.Add(value); } sb.AppendLine(string.Join(Separator.ToString(), values.ToArray())); } if (UseEofLiteral) { values.Clear(); if (UseLineNumbers) { values.Add(row++.ToString()); } values.Add("EOF"); sb.AppendLine(string.Join(Separator.ToString(), values.ToArray())); } return sb.ToString(); } /// /// Get Header /// /// private string GetHeader() { var header = _properties.Select(a => a.Name); if (UseLineNumbers) { header = new string[] { RowNumberColumnTitle }.Union(header); } return string.Join(Separator.ToString(), header.ToArray()); } } #pragma warning restore CA1305 // Specify IFormatProvider public class CsvIgnoreAttribute : Attribute { } public class InvalidCsvFormatException : Exception { /// /// Invalid Csv Format Exception /// /// message public InvalidCsvFormatException(string message) : base(message) { } public InvalidCsvFormatException(string message, Exception ex) : base(message, ex) { } } }