Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

CsvHelper 15.0.5 Reference: Reading, Writing, Mapping, and Configuration

Tech 2

CsvHelper is a .NET library for high-performance CSV parsing and generation, targeting .NET Stanadrd 2.0.

Modules

  • CsvHelper: Core API for parsing and writing CSV.
  • CsvHelper.Configuration: Runtime configuration surface.
  • CsvHelper.Configuration.Attributes: Attribute-based model configuration.
  • CsvHelper.Expressions: Internals for generated expression trees.
  • CsvHelper.TypeConversion: Built-in and extensible type converters.

Reading

Sample model

public class Foo
{
    public int ID { get; set; }
    public string Name { get; set; }
}

Sample CSV

ID,Name
1,Tom
2,Jerry

Read all records (streamed)

using var sr = new StreamReader("foo.csv");
using var csv = new CsvReader(sr, CultureInfo.InvariantCulture);

IEnumerable<Foo> rows = csv.GetRecords<Foo>();
// Enumerate to process; not materialized until iterated.
foreach (var r in rows)
{
    // handle r
}

Blank lines are skipped by default; lines containing only whitespace are not considered blank and can trigger errors. CSV exported by spreadsheet tools may produce lines containing only delimiters; treat those as bad data or adjust configuration if needed.

Read record-by-record

using var sr = new StreamReader("foo.csv");
using var csv = new CsvReader(sr, CultureInfo.InvariantCulture);

while (csv.Read())
{
    var item = csv.GetRecord<Foo>();
    // use item
}

GetRecords<T>() already streams via yield; explicit Read/GetRecord is only needed when you want fine-grained control per row.

Read individual fields

using var sr = new StreamReader("foo.csv");
using var csv = new CsvReader(sr, CultureInfo.InvariantCulture);

csv.Read();          // advance to header row
csv.ReadHeader();    // cache header names

while (csv.Read())
{
    int id = csv.GetField<int>(0);       // by index
    string name = csv.GetField<string>("Name"); // by header
}
  • With out ReadHeader, lookups by header name will fail.
  • TryGetField avoids exceptions when data is malformed or missing.
if (csv.TryGetField(0, out int id))
{
    // id available
}

Writing

Write all records

var items = new List<Foo>
{
    new Foo { ID = 1, Name = "Tom" },
    new Foo { ID = 2, Name = "Jerry" },
};

using var sw = new StreamWriter("foo.csv");
using var csv = new CsvWriter(sw, CultureInfo.InvariantCulture);

csv.WriteRecords(items); // writes header + all rows

Write one record at a time

using var sw = new StreamWriter("foo.csv");
using var csv = new CsvWriter(sw, CultureInfo.InvariantCulture);

// Optionally write a header first
csv.WriteHeader<Foo>();
csv.NextRecord();

foreach (var row in items)
{
    csv.WriteRecord(row);
    csv.NextRecord(); // commit line break per record
}

Write field-by-field

using var sw = new StreamWriter("foo.csv");
using var csv = new CsvWriter(sw, CultureInfo.InvariantCulture);

csv.WriteHeader<Foo>();
csv.NextRecord();

foreach (var row in items)
{
    csv.WriteField(row.ID);
    csv.WriteField(row.Name);
    csv.NextRecord();
}

Attributes (Annotations)

Index

Control column order and support headerless files.

public class Foo
{
    [Index(0)]
    public int ID { get; set; }

    [Index(1)]
    public string Name { get; set; }
}

using var sr = new StreamReader("foo.csv");
using var csv = new CsvReader(sr, CultureInfo.InvariantCulture)
{
    Configuration = { HasHeaderRecord = false }
};

var list = csv.GetRecords<Foo>().ToList();
  • Set HasHeaderRecord = false to avoid skipping the first line when no header exists.
  • When writing, columns follow Index order. Disable header if you don’t want one emitted.

Name

Bind a property to a differently named header.

public class Foo
{
    [Name("id")]
    public int ID { get; set; }

    [Name("name")]
    public string Name { get; set; }
}

NameIndex

Disambiguate duplicate header names.

public class Person
{
    [Name("Name"), NameIndex(0)]
    public string FirstName { get; set; }

    [Name("Name"), NameIndex(1)]
    public string LastName { get; set; }
}

Ignore

Exclude a property from read/write.

public class WithInternal
{
    public string Visible { get; set; }

    [Ignore]
    public string Hidden { get; set; }
}

Optional

Skip a property when no matching column exists.

public class MaybeExtended
{
    public string Core { get; set; }

    [Optional]
    public string Remarks { get; set; }
}

Default

Provide a default value when the incoming field is empty.

public class Defaults
{
    [Default("N/A")] // applied on read when field is empty
    public string Note { get; set; }
}

Default affects reads only; it does not replace nulls on write.

NullValues

Map specific literal values to null during reads.

public class Nullables
{
    [NullValues("None", "none", "Null", "null")]
    public string AliasOfNull { get; set; }
}

Empty CSV fields read as "" (empty string) by default. With NullValues, matching tokens are converted to null. If combined with Default, the null mapping takes precedence and the Default may not apply.

NullValues does not affect writes.

Constant

Force a fixed value for a property regardless of CSV content during both read and write.

public class FixedValue
{
    [Constant("Always")] 
    public string Tag { get; set; }
}

Format

Specify formatting/parsing patterns for conversion.

public class Formats
{
    [Format("0.00")]
    public decimal Amount { get; set; }

    [Format("yyyy-MM-dd HH:mm:ss")]
    public DateTime Timestamp { get; set; }
}

BooleanTrueValues and BooleanFalseValues

Define custom boolean literals.

public class Flags
{
    [BooleanTrueValues("yes")]
    [BooleanFalseValues("no")]
    public bool Vip { get; set; }
}

NumberStyles

Control numeric parsing styles; combine with Format for write-side formatting.

public class HexData
{
    [Format("X2")]
    [NumberStyles(NumberStyles.HexNumber)]
    public int Data { get; set; }
}

NumberStyles (e.g., HexNumber/AllowHexSpecifier) applies on read. Use Format to control write representation.

Mapping (ClassMap)

Use ClassMap when attributes aren’t possible or desired.

public class Foo2
{
    public int ID { get; set; }
    public string Name { get; set; }
    public decimal Amount { get; set; }
    public DateTime JoinTime { get; set; }
    public string Msg { get; set; }
    public string Msg2 { get; set; }
    public bool Vip { get; set; }
    public string Remarks { get; set; }
    public string None { get; set; }
    public int Data { get; set; }
}

public class Foo2Map : ClassMap<Foo2>
{
    public Foo2Map()
    {
        Map(x => x.ID).Index(0).Name("id");
        Map(x => x.Name).Index(1).Name("name");
        Map(x => x.Amount).TypeConverterOption.Format("0.00");
        Map(x => x.JoinTime).TypeConverterOption.Format("yyyy-MM-dd HH:mm:ss");
        Map(x => x.Msg).Default("Hello");
        Map(x => x.Msg2).Ignore();
        Map(x => x.Vip)
            .TypeConverterOption.BooleanValues(true, true, new[] { "yes" })
            .TypeConverterOption.BooleanValues(false, true, new[] { "no" });
        Map(x => x.Remarks).Optional();
        Map(x => x.None).TypeConverterOption.NullValues("None", "none", "Null", "null");
        Map(x => x.Data)
            .TypeConverterOption.NumberStyles(NumberStyles.HexNumber)
            .TypeConverterOption.Format("X2");
    }
}

Register the map before reading/writing.

csv.Configuration.RegisterClassMap<Foo2Map>();

ConvertUsing

Apply custom conversion logic.

// constant value
Map(m => m.Amount).ConvertUsing(row => 3.00m);

// compose full name from multiple columns
Map(m => m.Name).ConvertUsing(row =>
{
    var first = row.GetField<string>("FirstName");
    var last = row.GetField<string>("LastName");
    return $"{first} {last}";
});

// map to a collection
Map(m => m.Msg2).ConvertUsing(row => new List<string> { row.GetField<string>("Msg") }.First());

Configuration

Delimiter

csv.Configuration.Delimiter = ",";

HasHeaderRecord

Use or skip the first row as header.

csv.Configuration.HasHeaderRecord = false;

IgnoreBlankLines

Control whether to skip empty lines (true by default).

csv.Configuration.IgnoreBlankLines = false;

Whitespace-only row are not considered blank.

AllowComments

Enable comment parsing.

csv.Configuration.AllowComments = true;

Commment

Choose the comment prefix character (default '#').

csv.Configuration.Comment = '/';

BadDataFound

Hook for reporting malformed data.

csv.Configuration.BadDataFound = context =>
{
    // context.RawRecord, context.Field, context.RawRow, etc.
    // log or collect diagnostics
};

IgnoreQuotes

Treat quotes as ordinary characters when parsing.

csv.Configuration.IgnoreQuotes = true;
  • Default is false: quotes must be escaped by doubling (e.g., """ for a literal ").
  • When true, quote characters are not treated specially during parsing.
  • CsvWriter does not expose IgnoreQuotes; it will escape quotes as needed on write.

TrimOptions

Trim whitespace.

csv.Configuration.TrimOptions = TrimOptions.Trim;

PrepareHeaderForMatch

Normalize headers and property names before matching.

csv.Configuration.PrepareHeaderForMatch = (header, index) => header.Replace(" ", string.Empty).ToLowerInvariant();
Tags: csvhelper

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.