Skip to content

Latest commit

 

History

10 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Icon LS.FileReader

A lightweight C# utility to read CSV, Excel (XLSX), and JSON files into strongly-typed models using attribute-based mapping and optional streaming support — and to write those models back out as CSV under the same column names.

  • ✅ Supports .csv, .xlsx, .json
  • ✅ Attribute-based property mapping
  • Read<T>() for small files (sync, memory-based)
  • ReadAsync<T>() for large files (async stream)
  • WriteCsv<T>() for export — same attribute, so import and export never drift apart
  • ✅ RFC 4180 CSV parsing: quoted separators, quoted line breaks, "" escapes
  • ✅ Culture-invariant parsing and formatting
  • ✅ Built with clean architecture & extensibility
  • ✨ Created with AI-assisted engineering (OpenAI ChatGPT)
  • ⚙️ Powered by ExcelDataReader

🔧 Installation

dotnet add package LS.FileReader

🚀 Quick Start

1. Define your model with HeaderColumnAttribute

using LS.FileReader.Attributes;

public class PersonDto
{
    [HeaderColumn("Name", "FullName")]
    public string Name { get; set; }

    [HeaderColumn("Age")]
    public int Age { get; set; }

    [HeaderColumn("DOB")]
    public DateTime BirthDate { get; set; }
}

2. Use ILsFileReader.Read<T>() to fully load and parse the file

var reader = new LsFileReader();

var result = reader.Read<PersonDto>(formFile); // Supports .csv, .xlsx, .json

foreach (var person in result.Data)
{
    Console.WriteLine($"{person.Name}, {person.Age}");
}

foreach (var error in result.ErrorRows)
{
    Console.WriteLine($"Row {error.RowIndex} failed: {error.Error}");
}

3. Use ReadAsync<T>() for large files (streaming)

await foreach (var row in reader.ReadAsync<PersonDto>(formFile))
{
    if (row.IsSuccess)
    {
        var person = row.Data;
        // Process person
        // Do something at here, add to db / signal r notify
    }
    else
    {
        Console.WriteLine($"Row {row.RowIndex} failed: {row.Error}");
    }
}

4. Use ReadAsync<T>() + FileEstimateHelper for large files (streaming) and batch process

var batch = new List<PersonDto>();
var batchSize = 100;
var lastNotifyTime = DateTime.UtcNow;
var estimatedTotalRows = 0;

await foreach (var row in reader.ReadAsync<PersonDto>(formFile))
{
    if (!row.IsSuccess)
    {
        Console.WriteLine($"Row {row.RowIndex} failed: {row.Error}");
        continue;
    }

    // Estimate total rows once (based on file size and column count)
    if (estimatedTotalRows == 0)
    {
        estimatedTotalRows = FileEstimateHelper.EstimateTotalRows(formFile, row.ColumnCount);
    }

    batch.Add(row.Data);

    if (batch.Count >= batchSize)
    {
        await db.SaveBatchAsync(batch); // Replace with your batch save logic
        batch.Clear();
    }

    // Notify progress every 250ms
    if ((DateTime.UtcNow - lastNotifyTime).TotalMilliseconds >= 250)
    {
        double percent = (double)row.RowIndex / estimatedTotalRows * 100;
        await notifier.SendProgressAsync(connectionId, percent);
        lastNotifyTime = DateTime.UtcNow;
    }
}

// Flush remaining rows
if (batch.Count > 0)
{
    await db.SaveBatchAsync(batch);
}

5. Use ILsFileWriter.WriteCsv<T>() to export

var writer = new LsFileWriter();

byte[] csv = writer.WriteCsv(people);

return File(csv, "text/csv", "people.csv");

The header row comes from the first alias of the same HeaderColumnAttribute the reader matches against (a property with no attribute uses its own name). So the file you export is a file you can edit and import straight back — a column cannot be renamed on one side only.

Values holding ,, ", CR or LF are quoted and escaped; null is written as an empty cell; the output carries a UTF-8 BOM so Excel opens non-ASCII text correctly.

To write into a stream you already own (the stream is left open):

writer.WriteCsv(people, response.Body);

📂 Supported Formats

Format Method Notes
.csv Read<T>(), ReadAsync<T>(), WriteCsv<T>() Header must match column/alias
.xlsx Read<T>(), ReadAsync<T>() First sheet is used by default
.json Read<T>() only Must be JSON array

📘 Notes

  • All file reads are limited to 10MB to avoid memory spikes.
  • Property names are matched case-insensitively to headers or aliases.
  • Internally uses System.Text.Json for JSON and ExcelDataReader for Excel files.

Cell values

  • A blank cell means "no value", not "bad value". For a string or Nullable<T> property it maps to null and the row succeeds. Only a non-nullable value type still fails the row — there is nowhere to put "nothing".
  • Numbers and dates are parsed and written with InvariantCulture, so a file exported on one machine imports identically on another.
  • Booleans accept true/false, yes/no, y/n, 1/0 (case-insensitive), and are written as true/false.
  • Get-only computed properties are skipped rather than throwing.

CSV specifics

  • Parsing follows RFC 4180: a quoted field may contain commas, line breaks, and "" for a literal quote. A UTF-8 BOM is consumed if present.
  • RowIndex therefore counts records, not physical lines — a quoted field spanning a line break is one row, matching what the user sees in the spreadsheet.
  • A blank line is skipped; a line of nothing but separators (,,) is a row of empty values, not a blank line.

🕘 Version history

Version Change
1.1.0 Added ILsFileWriter.WriteCsv<T>(). CSV parsing rewritten to RFC 4180 (previously line.Split(','), which silently shifted columns when a field held a comma). Blank cells on optional properties are now null instead of failing the whole row. Culture-invariant number/date handling; lenient booleans.
1.0.2 Performance tuning.

👤 Author

  • Laoseng (developer)
  • OpenAI ChatGPT (AI-assisted architecture & implementation)

📝 License

MIT License
Includes Excel parsing powered by ExcelDataReader (MIT)

About

A lightweight C# utility to read CSV, Excel (XLSX), and JSON files into strongly-typed models using attribute-based mapping and optional streaming support.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages