Lyo.Csv
2.0.0
dotnet add package Lyo.Csv --version 2.0.0
NuGet\Install-Package Lyo.Csv -Version 2.0.0
<PackageReference Include="Lyo.Csv" Version="2.0.0" />
<PackageVersion Include="Lyo.Csv" Version="2.0.0" />
<PackageReference Include="Lyo.Csv" />
paket add Lyo.Csv --version 2.0.0
#r "nuget: Lyo.Csv, 2.0.0"
#:package Lyo.Csv@2.0.0
#addin nuget:?package=Lyo.Csv&version=2.0.0
#tool nuget:?package=Lyo.Csv&version=2.0.0
Lyo.Csv
In-house implementation of Lyo.Csv.Models. CsvService composes a CsvWriter and CsvReader over an internal field tokenizer/writer and a cached type binder. Multi-targets net10.0;netstandard2.0. Async, streaming, and option-based overloads exist only on net10.0.
Features
- Strongly-typed read/write through
IEnumerable<T>/List<T>andIAsyncEnumerable<T>(net10). - Read and write a row/column dictionary (
IReadOnlyDictionary<int, IReadOnlyDictionary<int, string>>). - Round-trip
Lyo.DataTable.Models.DataTableincludingFooter(ParseFileAsDataTablewithhasFooterRow;ExportToCsvFromDataTablealways appends footer when present; plus an HTML helperExportToHtmlTable). - CSV → DataTable pooling through
CsvOptions.Pooling/CsvParseOptions.Pooling(defaultsPoolValues=false; estimate iscols × (rows+1)after the full CSV is buffered). - Export selected properties (
IReadOnlyList<PropertyInfo>), custom headers (IReadOnlyDictionary<string, PropertyInfo>), or formatter delegates (IReadOnlyDictionary<string, Func<T, string>>). ParseFromUrl*helpers download from a URL and share an optional injectedHttpClient.- File append, combine, and split (async,
net10.0only). IAsyncEnumerable<T>and string-row streaming reads,IAsyncEnumerableexports, chunked processing, statistics, schema validation, column-mapping parses, and CSV-to-CSV comparison.- Dialect through
CsvOptions: delimiter, quote, escape, comments, trim, blank-line skip, column-count detection, culture, encoding. - Header rename through
[CsvColumn]; encoding throughSetEncoding/SetOptions;CodePagesEncodingProvideris registered in the service constructor. - Ships
ICsvValueConverterimplementations:DecimalCsvConverter,Int32CsvConverter,Int64CsvConverter,YesNoBoolCsvConverter. - When wrapping failures in
Result<T>, usesCsvErrorCodesconstants (CSV_EXPORT_FAILED,CSV_PARSE_FAILED,CSV_OPERATION_CANCELLED,CSV_FILE_OPERATION_FAILED,CSV_VALIDATION_FAILED).
Examples
Add CsvService in DI
using Lyo.Csv;
using Lyo.Csv.Models;
services.AddCsvService();
services.AddCsvService(o => {
o.Pooling.PoolValues = true; // opt in for high-duplication grids
o.Pooling.PoolingCellThreshold = 512;
o.Delimiter = ";";
o.HasHeaderRecord = true;
o.AllowComments = true;
});
services.AddCsvService(new CsvOptions { Delimiter = "|", Quote = '"', Escape = '"' });
services.AddCsvService((provider, options) => {
var feature = provider.GetRequiredService<IFeatureFlags>();
options.Delimiter = feature.UseSemicolons ? ";" : ",";
});
Export and import
public sealed class ReportingService(ICsvService csv)
{
public async Task ExportAsync(IEnumerable<Person> rows, Stream output, CancellationToken ct)
=> await csv.ExportToCsvStreamAsync(rows, output, ct);
public async Task<List<Person>> ImportAsync(string path, CancellationToken ct)
=> await csv.ParseFileAsync<Person>(path, ct);
}
public sealed record Person(int Id, string Name, int Age);
Dialect and [CsvColumn]
csv.SetEncoding(new UTF8Encoding(encoderShouldEmitUTF8Identifier: true));
csv.SetOptions(new CsvOptions {
Delimiter = ";",
TrimFields = true,
IgnoreBlankLines = true,
AllowComments = true,
Comment = '#',
});
public sealed class Person
{
[CsvColumn(Ignore = true)]
public int Id { get; set; }
[CsvColumn("Full Name")]
public string Name { get; set; }
[CsvColumn("Years Old")]
public int Age { get; set; }
}
Pick and rename columns
IReadOnlyList<PropertyInfo> selected = [
typeof(Person).GetProperty(nameof(Person.Name))!,
typeof(Person).GetProperty(nameof(Person.Age))!,
];
csv.ExportToCsv(rows, selected, "out.csv");
IReadOnlyDictionary<string, PropertyInfo> namedColumns = new Dictionary<string, PropertyInfo> {
["Full Name"] = typeof(Person).GetProperty(nameof(Person.Name))!,
["Years"] = typeof(Person).GetProperty(nameof(Person.Age))!,
};
await csv.ExportToCsvStreamAsync(rows, namedColumns, stream, ct);
IReadOnlyDictionary<string, Func<Person, string>> formatters = new Dictionary<string, Func<Person, string>> {
["Display"] = p => $"{p.Name} ({p.Age})",
["Id"] = p => p.Id.ToString("D6"),
};
await csv.ExportToCsvStreamAsync(rows, formatters, stream, ct);
DataTable, dictionary, and HTML
Result<DataTable> parsed = csv.ParseFileAsDataTable("in.csv", hasHeaderRow: true, hasFooterRow: true);
csv.ExportToCsvFromDataTable(parsed.ValueOrThrow(), "out.csv"); // writes Footer as trailing row when present
var grid = csv.ParseFileAsDictionary("in.csv");
csv.ExportToCsvFromDictionary(grid, "out.csv", hasHeaderRow: true, hasFooterRow: true);
string html = csv.ExportToHtmlTable(File.ReadAllBytes("in.csv"), hasHeaderRow: true, hasFooterRow: true);
Stream, stats, validate, compare
await foreach (var row in csv.ParseFileStreamingAsync<Person>("big.csv", new CsvParseOptions {
ContinueOnError = true,
OnError = err => log.LogWarning("Row {Row}: {Message}", err.RowNumber, err.Message),
RowFilter = cells => cells["Status"] == "active",
MaxRows = 100_000,
}, ct)) {
// process row
}
await foreach (var cells in csv.ParseFileRowsStreamingAsync("big.csv", ct)) {
// untyped string cells
}
await csv.ExportToCsvStreamAsync(asyncRows, stream, ct);
CsvStatistics stats = await csv.GetStatisticsAsync("big.csv", ct);
await csv.ProcessFileInChunksAsync<Person>(
"big.csv",
chunkSize: 1_000,
processChunk: async batch => await sink.WriteAsync(batch),
ct: ct);
ValidationResult validation = await csv.ValidateAsync("in.csv", new CsvSchema {
RequireAllColumns = true,
AllowExtraColumns = false,
Columns = [
new CsvColumn { Name = "Id", IsRequired = true },
new CsvColumn { Name = "Name", IsRequired = true },
],
}, ct);
CsvComparisonResult diff = await csv.CompareFilesAsync("v1.csv", "v2.csv", keyColumn: "Id", ct);
Append, combine, and split (net10.0)
await csv.AppendToCsvAsync(rows, "log.csv", includeHeaderIfMissing: true, ct);
await csv.CombineCsvFilesAsync(parts, "merged.csv", includeHeaders: true, ct);
await csv.SplitCsvFileAsync("merged.csv", rowsPerFile: 10_000, outputDirectory: "chunks", ct);
Benchmarks
UTF-8 export of 100,000 sample rows takes tens of milliseconds.
- Portfolio suite:
csv - CSV UTF-8 export
DI registration
AddCsvService adds a singleton CsvService and maps ICsvService, ICsvWriter, and ICsvReader to that instance. Overloads take Action<CsvOptions>, an options instance, Action<IServiceProvider, CsvOptions>, and AddCsvServiceFromConfiguration (binds Csv and optional DataTablePooling sections). Default CSV pooling stays off.
Where exports can go
Every export path has file / stream / TextWriter / string / byte array overloads, sync on all TFMs and async on net10.0:
csv.ExportToCsv(rows, "out.csv");
csv.ExportToCsvStream(rows, stream);
csv.ExportToCsv(rows, textWriter);
string text = csv.ExportToCsvString(rows);
byte[] bytes = csv.ExportToCsvBytes(rows);
await csv.ExportToCsvAsync(rows, "out.csv", ct);
await csv.ExportToCsvStreamAsync(rows, stream, ct);
string textAsync = await csv.ExportToCsvStringAsync(rows, ct);
byte[] bytesAsync = await csv.ExportToCsvBytesAsync(rows, ct);
URLs and batch parse
Result<DataTable> table = await csv.ParseFromUrlAsDataTableAsync(url, hasHeaderRow: true, hasFooterRow: true, ct);
List<Person> rows = await csv.ParseFromUrlAsync<Person>(url, ct);
IReadOnlyList<Result<DataTable>> results =
await csv.BatchParseFilesAsDataTableAsync(paths, hasHeaderRow: true, hasFooterRow: true, ct);
If the constructor is not given an HttpClient, a fresh one is created per URL call and disposed afterward. Inject one via DI. Pass hasFooterRow: true when the last physical row should become DataTable.Footer (default false).
Default dialect
Default CsvOptions use a comma delimiter, RFC doubled quotes, header row on, blank-line skip, field trim, and DetectColumnCountChanges (throws CsvBadDataException on uneven rows). Header names match properties case-insensitively after trim. Rename with [CsvColumn]. Prefer typed or IAsyncEnumerable streams for large files. ParseAsDataTable materializes the entire grid.
Public types
CsvErrorCodesCsvServiceDecimalCsvConverterExtensionsInt32CsvConverterInt64CsvConverterIsExternalInitYesNoBoolCsvConverter
License
Copyright © Lyo
Dependencies
Generated from ProjectReference / PackageReference (same model as docs/Lyo.ProjectGraph.html).
Lyo.Common.Core(direct, lyo)Lyo.Configuration(direct, lyo)Lyo.Csv.Models(direct, lyo)Lyo.Exceptions(direct, lyo)Lyo.Result(direct, lyo)Microsoft.Extensions.Configuration10.0.5(direct, microsoft)Microsoft.Extensions.Configuration.Binder10.0.5(direct, microsoft)Microsoft.Extensions.DependencyInjection.Abstractions10.0.5(direct, microsoft)Microsoft.Extensions.Logging.Abstractions10.0.5(direct, microsoft)Microsoft.Extensions.Options10.0.5(direct, microsoft)System.Text.Encoding.CodePages10.0.5(direct, microsoft)Lyo.DataTable.Models(transitive, lyo)Microsoft.Bcl.AsyncInterfaces10.0.5(transitive, microsoft, netstandard2.0)System.Memory4.6.3(transitive, microsoft, netstandard2.0)System.Text.Json10.0.5(transitive, microsoft, netstandard2.0)
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- Lyo.Common.Core (>= 2.0.0)
- Lyo.Configuration (>= 2.0.0)
- Lyo.Csv.Models (>= 2.0.0)
- Lyo.Exceptions (>= 2.0.0)
- Lyo.Result (>= 2.0.0)
- Microsoft.Extensions.Configuration (>= 10.0.5)
- Microsoft.Extensions.Configuration.Binder (>= 10.0.5)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.5)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.5)
- Microsoft.Extensions.Options (>= 10.0.5)
- System.Text.Encoding.CodePages (>= 10.0.5)
-
net10.0
- Lyo.Common.Core (>= 2.0.0)
- Lyo.Configuration (>= 2.0.0)
- Lyo.Csv.Models (>= 2.0.0)
- Lyo.Exceptions (>= 2.0.0)
- Lyo.Result (>= 2.0.0)
- Microsoft.Extensions.Configuration (>= 10.0.5)
- Microsoft.Extensions.Configuration.Binder (>= 10.0.5)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.5)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.5)
- Microsoft.Extensions.Options (>= 10.0.5)
NuGet packages (3)
Showing the top 3 NuGet packages that depend on Lyo.Csv:
| Package | Downloads |
|---|---|
|
Lyo.FileStorage.Web.Components
Reusable Blazor components for file storage: upload/save, path tree browser, DEK/KEK migration and rotation, metadata/expected-storage browser grids. |
|
|
Lyo.Reporting.Postgres
PostgreSQL reporting data layer with EF Core, optional auto-migrations, and report generation orchestration. |
|
|
Lyo.Api.Export.Csv
CSV export format handler for Lyo.Api export service. |
GitHub repositories
This package is not used by any popular GitHub repositories.