diff --git a/README.md b/README.md index ac08c1cf..7e7aa85f 100644 --- a/README.md +++ b/README.md @@ -1250,7 +1250,16 @@ var config = new MiniExcelLibs.Csv.CsvConfiguration() MiniExcel.SaveAs(path, value,excelType:ExcelType.CSV, configuration: config); ``` +#### Read empty string as null +By default, empty values are mapped to string.Empty. You can modify this behavior + +```csharp +var config = new MiniExcelLibs.Csv.CsvConfiguration() +{ + ReadEmptyStringAsNull = true +}; +``` ### DataReader diff --git a/src/MiniExcel/Csv/CsvConfiguration.cs b/src/MiniExcel/Csv/CsvConfiguration.cs index ba2b6448..90e7a1ab 100644 --- a/src/MiniExcel/Csv/CsvConfiguration.cs +++ b/src/MiniExcel/Csv/CsvConfiguration.cs @@ -10,6 +10,7 @@ public class CsvConfiguration : Configuration public char Seperator { get; set; } = ','; public string NewLine { get; set; } = "\r\n"; + public bool ReadEmptyStringAsNull { get; set; } = false; public bool AlwaysQuote { get; set; } = false; public Func SplitFn { get; set; } public Func StreamReaderFunc { get; set; } = (stream) => new StreamReader(stream, _defaultEncoding); diff --git a/src/MiniExcel/Csv/CsvReader.cs b/src/MiniExcel/Csv/CsvReader.cs index 01616ada..7a0d0688 100644 --- a/src/MiniExcel/Csv/CsvReader.cs +++ b/src/MiniExcel/Csv/CsvReader.cs @@ -59,8 +59,17 @@ public IEnumerable> Query(bool useHeaderRow, string //body { var cell = CustomPropertyHelper.GetEmptyExpandoObject(read.Length - 1, 0); - for (int i = 0; i <= read.Length - 1; i++) - cell[ColumnHelper.GetAlphabetColumnName(i)] = read[i]; + if (_config.ReadEmptyStringAsNull) + { + for (int i = 0; i <= read.Length - 1; i++) + cell[ColumnHelper.GetAlphabetColumnName(i)] = read[i]?.Length == 0 ? null : read[i]; + } + else + { + for (int i = 0; i <= read.Length - 1; i++) + cell[ColumnHelper.GetAlphabetColumnName(i)] = read[i]; + } + yield return cell; } } diff --git a/tests/MiniExcelTests/MiniExcelCsvAsycTests.cs b/tests/MiniExcelTests/MiniExcelCsvAsycTests.cs index 8ee0c1b2..5a467ec1 100644 --- a/tests/MiniExcelTests/MiniExcelCsvAsycTests.cs +++ b/tests/MiniExcelTests/MiniExcelCsvAsycTests.cs @@ -269,5 +269,55 @@ await MiniExcel.SaveAsAsync(path, new[] { File.Delete(path); } + + [Fact()] + public async Task CsvReadEmptyStringAsNullTest() + { + var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}.csv"); + await MiniExcel.SaveAsAsync(path, new[] { + new { c1 = "A1" ,c2 = (string)null}, + new { c1 = (string)null ,c2 = (string)null}, + }); + + using (var stream = File.OpenRead(path)) + { + var rows = stream.Query(excelType: ExcelType.CSV).ToList(); + Assert.Equal("A1", rows[0].c1); + Assert.Equal(string.Empty, rows[0].c2); + Assert.Equal(string.Empty, rows[1].c1); + Assert.Equal(string.Empty, rows[1].c2); + } + + { + var rows = MiniExcel.Query(path, excelType: ExcelType.CSV).ToList(); + Assert.Equal("A1", rows[0].c1); + Assert.Equal(string.Empty, rows[0].c2); + Assert.Equal(string.Empty, rows[1].c1); + Assert.Equal(string.Empty, rows[1].c2); + } + + var config = new MiniExcelLibs.Csv.CsvConfiguration() + { + ReadEmptyStringAsNull = true + }; + using (var stream = File.OpenRead(path)) + { + var rows = stream.Query(excelType: ExcelType.CSV, configuration: config).ToList(); + Assert.Equal("A1", rows[0].c1); + Assert.Null(rows[0].c2); + Assert.Null(rows[1].c1); + Assert.Null(rows[1].c2); + } + + { + var rows = MiniExcel.Query(path, excelType: ExcelType.CSV, configuration: config).ToList(); + Assert.Equal("A1", rows[0].c1); + Assert.Null(rows[0].c2); + Assert.Null(rows[1].c1); + Assert.Null(rows[1].c2); + } + + File.Delete(path); + } } } \ No newline at end of file