mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2025-01-19 05:33:31 +01:00
Use CsvHelper for generating CSV
This commit is contained in:
parent
17bcfe154c
commit
26112a1ed6
@ -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);
|
||||
});
|
||||
}
|
||||
|
@ -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">
|
||||
|
@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user