Use CsvHelper for generating CSV

This commit is contained in:
nicolas.dorier 2020-07-30 10:01:56 +09:00
parent 17bcfe154c
commit 26112a1ed6
No known key found for this signature in database
GPG Key ID: 6618763EF09186FE
4 changed files with 16 additions and 152 deletions

View File

@ -2362,11 +2362,10 @@ namespace BTCPayServer.Tests
user.GetController<InvoiceController>().Export("csv").GetAwaiter().GetResult();
var paidresult = Assert.IsType<ContentResult>(exportResultPaid);
Assert.Equal("application/csv", paidresult.ContentType);
Assert.Contains($",\"orderId\",\"{invoice.Id}\",", paidresult.Content);
Assert.Contains($",\"On-Chain\",\"BTC\",\"0.0991\",\"0.0001\",\"5000.0\"", paidresult.Content);
Assert.Contains($",\"USD\",\"5.00",
paidresult.Content); // Seems hacky but some plateform does not render this decimal the same
Assert.Contains($"0\",\"500.0\",\"\",\"Some ``, description\",\"new (paidPartial)\"",
Assert.Contains($",orderId,{invoice.Id},", paidresult.Content);
Assert.Contains($",On-Chain,BTC,0.0991,0.0001,5000.0", paidresult.Content);
Assert.Contains($",USD,5.00", paidresult.Content); // Seems hacky but some plateform does not render this decimal the same
Assert.Contains("0,,\"Some \"\", description\",new (paidPartial),new,paidPartial",
paidresult.Content);
});
}

View File

@ -50,6 +50,7 @@
<PackageReference Include="BuildBundlerMinifier" Version="3.2.435" />
<PackageReference Include="BundlerMinifier.Core" Version="3.2.435" />
<PackageReference Include="BundlerMinifier.TagHelpers" Version="3.2.435" />
<PackageReference Include="CsvHelper" Version="15.0.5" />
<PackageReference Include="HtmlSanitizer" Version="4.0.217" />
<PackageReference Include="Microsoft.Extensions.Logging.Filter" Version="1.1.2" />
<PackageReference Include="Microsoft.NetCore.Analyzers" Version="2.9.8">

View File

@ -1,143 +0,0 @@
using System;
using System.Collections.Generic;
using System.Globalization;
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
{
/// <summary>
/// Serialize and Deserialize Lists of any object type to CSV.
/// </summary>
public class CsvSerializer<T> where T : class, new()
{
private readonly List<PropertyInfo> _properties;
public bool IgnoreEmptyLines { get; set; } = true;
public bool IgnoreReferenceTypesExceptString { get; set; } = true;
public string NewlineReplacement { get; set; } = ((char)0x254).ToString(CultureInfo.InvariantCulture);
public char Separator { get; set; } = ',';
public string RowNumberColumnTitle { get; set; } = "RowNumber";
public bool UseLineNumbers { get; set; } = false;
public bool UseEofLiteral { get; set; } = false;
/// <summary>
/// Csv Serializer
/// Initialize by selected properties from the type to be de/serialized
/// </summary>
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<CsvIgnoreAttribute>() == null
select a;
_properties = r.ToList();
}
/// <summary>
/// Serialize
/// </summary>
/// <param name="stream">stream</param>
/// <param name="data">data</param>
public string Serialize(IList<T> data)
{
var sb = new StringBuilder();
var values = new List<string>();
sb.AppendLine(GetHeader());
var row = 1;
foreach (var item in data)
{
values.Clear();
if (UseLineNumbers)
{
values.Add(row++.ToString(CultureInfo.InvariantCulture));
}
foreach (var p in _properties)
{
var raw = p.GetValue(item);
var value = raw == null ? "" :
raw.ToString()
.Replace("\"", "``", StringComparison.OrdinalIgnoreCase)
.Replace(Environment.NewLine, NewlineReplacement, StringComparison.OrdinalIgnoreCase);
value = String.Format(CultureInfo.InvariantCulture, "\"{0}\"", value);
values.Add(value);
}
sb.AppendLine(String.Join(Separator.ToString(CultureInfo.InvariantCulture), values.ToArray()));
}
if (UseEofLiteral)
{
values.Clear();
if (UseLineNumbers)
{
values.Add(row++.ToString(CultureInfo.InvariantCulture));
}
values.Add("EOF");
sb.AppendLine(string.Join(Separator.ToString(CultureInfo.InvariantCulture), values.ToArray()));
}
return sb.ToString();
}
/// <summary>
/// Get Header
/// </summary>
/// <returns></returns>
private string GetHeader()
{
var header = _properties.Select(a => a.Name);
if (UseLineNumbers)
{
header = new string[] { RowNumberColumnTitle }.Union(header);
}
return string.Join(Separator.ToString(CultureInfo.InvariantCulture), header.ToArray());
}
}
public class CsvIgnoreAttribute : Attribute { }
public class InvalidCsvFormatException : Exception
{
/// <summary>
/// Invalid Csv Format Exception
/// </summary>
/// <param name="message">message</param>
public InvalidCsvFormatException(string message)
: base(message)
{
}
public InvalidCsvFormatException(string message, Exception ex)
: base(message, ex)
{
}
}
}

View File

@ -1,8 +1,10 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using BTCPayServer.Services.Rates;
using CsvHelper.Configuration;
using Newtonsoft.Json;
namespace BTCPayServer.Services.Invoices.Export
@ -42,10 +44,15 @@ namespace BTCPayServer.Services.Invoices.Export
private string processCsv(List<ExportInvoiceHolder> invoices)
{
var serializer = new CsvSerializer<ExportInvoiceHolder>();
var csv = serializer.Serialize(invoices);
return csv;
using StringWriter writer = new StringWriter();
using var csvWriter = new CsvHelper.CsvWriter(writer, new CsvConfiguration(CultureInfo.InvariantCulture), true);
csvWriter.WriteHeader<ExportInvoiceHolder>();
foreach (var invoice in invoices)
{
csvWriter.WriteRecord(invoice);
}
csvWriter.Flush();
return writer.ToString();
}
private IEnumerable<ExportInvoiceHolder> convertFromDb(InvoiceEntity invoice)