diff --git a/src/CsvSwan/Csv.cs b/src/CsvSwan/Csv.cs index c316f64..c5f3847 100644 --- a/src/CsvSwan/Csv.cs +++ b/src/CsvSwan/Csv.cs @@ -123,7 +123,7 @@ private Csv(Stream stream, CsvOptions options, bool canDisposeStream) this.canDisposeStream = canDisposeStream; reader = new CsvReader(this.stream, options); - accessor = new RowAccessor(this); + accessor = new RowAccessor(this, options); if (options.HasHeaderRow) { @@ -187,13 +187,15 @@ public void Dispose() public class RowAccessor { private readonly Csv csv; + private readonly CsvOptions options; private readonly object mutex = new object(); private readonly Dictionary typeFactories = new Dictionary(); - internal RowAccessor(Csv csv) + internal RowAccessor(Csv csv, CsvOptions options) { this.csv = csv ?? throw new ArgumentNullException(nameof(csv)); + this.options = options ?? new CsvOptions(); } /// @@ -209,9 +211,37 @@ public IReadOnlyList GetValues() /// Gets the value from the column at the given index as a . /// public short GetShort(int index, IFormatProvider formatProvider = null) + { + var value = GetNullableShort(index, formatProvider); + + if (value.HasValue) + { + return value.Value; + } + + if (options.DefaultNullValues) + { + return default; + } + + throw new InvalidOperationException($"Null or invalid value encountered for column {index} at row {csv.rowIndex}: '{csv.currentValues[index]}'."); + } + + /// + /// Gets the value from the column at the given index as an . + /// + public short? GetNullableShort(int index, IFormatProvider formatProvider = null) { GuardIndex(index); - return short.Parse(csv.currentValues[index], NumberStyles.Number, formatProvider ?? CultureInfo.InvariantCulture); + + var value = csv.currentValues[index]; + + if (string.IsNullOrEmpty(value)) + { + return null; + } + + return short.Parse(value, NumberStyles.Number, formatProvider ?? CultureInfo.InvariantCulture); } /// @@ -219,8 +249,19 @@ public short GetShort(int index, IFormatProvider formatProvider = null) /// public int GetInt(int index, IFormatProvider formatProvider = null) { - GuardIndex(index); - return int.Parse(csv.currentValues[index], NumberStyles.Number, formatProvider ?? CultureInfo.InvariantCulture); + var value = GetNullableInt(index, formatProvider); + + if (value.HasValue) + { + return value.Value; + } + + if (options.DefaultNullValues) + { + return default; + } + + throw new InvalidOperationException($"Null or invalid value encountered for column {index} at row {csv.rowIndex}: '{csv.currentValues[index]}'."); } /// @@ -245,8 +286,19 @@ public int GetInt(int index, IFormatProvider formatProvider = null) /// public long GetLong(int index, IFormatProvider formatProvider = null) { - GuardIndex(index); - return long.Parse(csv.currentValues[index], NumberStyles.Number, formatProvider ?? CultureInfo.InvariantCulture); + var value = GetNullableLong(index, formatProvider); + + if (value.HasValue) + { + return value.Value; + } + + if (options.DefaultNullValues) + { + return default; + } + + throw new InvalidOperationException($"Null or invalid value encountered for column {index} at row {csv.rowIndex}: '{csv.currentValues[index]}'."); } /// @@ -271,8 +323,19 @@ public long GetLong(int index, IFormatProvider formatProvider = null) /// public decimal GetDecimal(int index, IFormatProvider formatProvider = null) { - GuardIndex(index); - return decimal.Parse(csv.currentValues[index], NumberStyles.Number, formatProvider ?? CultureInfo.InvariantCulture); + var value = GetNullableDecimal(index, formatProvider); + + if (value.HasValue) + { + return value.Value; + } + + if (options.DefaultNullValues) + { + return default; + } + + throw new InvalidOperationException($"Null or invalid value encountered for column {index} at row {csv.rowIndex}: '{csv.currentValues[index]}'."); } /// @@ -297,8 +360,19 @@ public decimal GetDecimal(int index, IFormatProvider formatProvider = null) /// public double GetDouble(int index, IFormatProvider formatProvider = null) { - GuardIndex(index); - return double.Parse(csv.currentValues[index], NumberStyles.Number, formatProvider ?? CultureInfo.InvariantCulture); + var value = GetNullableDouble(index, formatProvider); + + if (value.HasValue) + { + return value.Value; + } + + if (options.DefaultNullValues) + { + return default; + } + + throw new InvalidOperationException($"Null or invalid value encountered for column {index} at row {csv.rowIndex}: '{csv.currentValues[index]}'."); } /// @@ -323,8 +397,19 @@ public double GetDouble(int index, IFormatProvider formatProvider = null) /// public float GetFloat(int index, IFormatProvider formatProvider = null) { - GuardIndex(index); - return float.Parse(csv.currentValues[index], NumberStyles.Number, formatProvider ?? CultureInfo.InvariantCulture); + var value = GetNullableFloat(index, formatProvider); + + if (value.HasValue) + { + return value.Value; + } + + if (options.DefaultNullValues) + { + return default; + } + + throw new InvalidOperationException($"Null or invalid value encountered for column {index} at row {csv.rowIndex}: '{csv.currentValues[index]}'."); } /// @@ -353,12 +438,17 @@ public bool GetBool(int index, IFormatProvider formatProvider = null) var nullable = GetNullableBool(index, formatProvider); - if (!nullable.HasValue) + if (nullable.HasValue) + { + return nullable.Value; + } + + if (options.DefaultNullValues) { - throw new NullReferenceException($"No bool value provided for bool at column index {index}, value was '{csv.currentValues[index]}'."); + return default; } - return nullable.Value; + throw new InvalidOperationException($"Null or invalid value encountered for column {index} at row {csv.rowIndex}: '{csv.currentValues[index]}'."); } /// diff --git a/src/CsvSwan/CsvOptions.cs b/src/CsvSwan/CsvOptions.cs index 2fa96ad..48ca7b8 100644 --- a/src/CsvSwan/CsvOptions.cs +++ b/src/CsvSwan/CsvOptions.cs @@ -59,6 +59,12 @@ public class CsvOptions /// public bool BackslashEscapesQuotes { get; set; } = false; + /// + /// For non-nullable types like s, s, s + /// use a default value when is encountered. + /// + public bool DefaultNullValues { get; set; } = true; + /// /// Create a new with the specified separator. /// @@ -68,8 +74,9 @@ public static CsvOptions WithSeparator(char separator, bool hasHeaderRow = false { Separator = separator, AreTextFieldsQuoted = true, - HasHeaderRow = hasHeaderRow + HasHeaderRow = hasHeaderRow, + DefaultNullValues = true }; } } -} \ No newline at end of file +}