Validate values and handle malformed input

Separate three failures: invalid configuration (before reading), malformed record syntax (while advancing), and invalid field conversion (when requesting a typed value). A recovery policy for CSV syntax does not automatically catch custom parser exceptions.

Optional fields and name-based null checks

using System.Text;
using PocketCsvReader.Ndjson.Configuration;

var json = new NdjsonReaderBuilder().WithDialect(d => d.WithLineTerminator("\n")).Build();
using var stream = new MemoryStream(Encoding.UTF8.GetBytes(
    "{\"name\":\"Ada\"}\n{\"name\":null}\n{\"id\":3}\n"));
using var reader = json.ToDataReader(stream);
while (reader.Read())
{
    if (!reader.TryGetOrdinal("name", out var ordinal)) Console.WriteLine("absent");
    else if (reader.IsDBNull("name")) Console.WriteLine("null");
    else Console.WriteLine(reader.GetString(ordinal!.Value));
}
Ada
null
absent

TryGetOrdinal returns false and a null ordinal for an absent label. GetOrdinal throws for absent names. Re-resolve after Read() for formats whose shape changes. These helpers belong to concrete readers rather than the standard IDataReader interface.

Validate a cell

Concrete readers and CSV batches expose IsValid(index) and IsValid(name). Validation uses the type reported by GetFieldType, its parser, format and applicable sanitization. Untyped string fields can be valid even when the text is not a date. Configure a date schema before using validation to check date syntax.

Expected conversion failures return false. Invalid names/ordinals, calls without a current row, configuration errors, and unexpected custom-parser failures still throw. Validation neither advances the reader nor caches the converted cell. Custom parsers run on each call and should avoid side effects. A later getter can still throw even after a prior validation attempt.

Null/missing values follow existing boxed-getter behavior: a CSV sanitized null that its boxed getter rejects is invalid, whereas native JSON null returned as DBNull.Value is valid. Arrays without whole-array overrides validate their elements. See null and missing values.

Recover from malformed CSV

using System.Text;
using PocketCsvReader;
using PocketCsvReader.Configuration;

var profile = new CsvProfile(new DialectDescriptorBuilder()
    .WithoutHeader().WithLineTerminator("\r\n").Build())
{
    BadDataPolicy = new BadDataPolicy(context => BadDataAction.SkipRecord, maximumErrors: 2)
};
var csv = new CsvReader(profile);
using var stream = new MemoryStream(Encoding.UTF8.GetBytes("a,b\r\n\"bad\"x,z\r\nc,d"));
foreach (var row in csv.ToArrayString(stream))
    Console.WriteLine(string.Join(",", row));
a,b
c,d
Action Behavior
Throw Propagate the malformed-record error; default without a recovery policy
SkipRecord Discard the malformed record and continue at recovery boundary
ReturnPartialRecord Return fields recovered from the malformed record; consumers must accept incomplete data
Stop Stop reading at the error

MaximumErrors limits reported malformed-input events. Exceeding it throws before another handler decision. Recovery is a CSV character-parser policy, not a promise that arbitrary corrupted input can be reconstructed. Native UTF-8 Auto falls back for profiles using a bad-data policy; forced byte mode rejects them. Other formats retain their documented error behavior.

Diagnostics and configuration errors

BadDataContext supplies record/line information, field index, offset, parser state, offending input and the exception. Enable WithPositionTracking() before reading for precise record starts and explicit offset units; see source positions. Legacy diagnostics without tracking can be approximate/record-relative.

Dialect Build() rejects invalid combinations described in CSV configuration errors. Fix configuration rather than trying to recover it as a data row. After an unrecovered parser error or input-I/O cancellation, stop using the reader and restart with a fresh reader and replayable input. Getter conversion errors do not themselves consume another record; handle them deliberately at the application level.

Reader filtering selects rows; it does not suppress conversion failures, configuration errors, or predicate exceptions. To reject malformed cell text deliberately, use a span predicate with TryParse. Rejected rows retain their source numbering for diagnostics.


Null, empty, and missing values
Source positions

Related Docs