diff --git a/src/ExcelDataReader.DataSet/ExcelDataReaderExtensions.cs b/src/ExcelDataReader.DataSet/ExcelDataReaderExtensions.cs index 9ac5b187..7dbfaa25 100644 --- a/src/ExcelDataReader.DataSet/ExcelDataReaderExtensions.cs +++ b/src/ExcelDataReader.DataSet/ExcelDataReaderExtensions.cs @@ -82,27 +82,45 @@ private static DataTable AsDataTable(IExcelDataReader self, ExcelDataTableConfig configuration.ReadHeaderRow(self); } - for (var i = 0; i < self.FieldCount; i++) + if (configuration.ReadHeader != null) { - if (configuration.FilterColumn != null && !configuration.FilterColumn(self, i)) + var dict = configuration.ReadHeader(self); + foreach (var kvp in dict) { - continue; + var columnIndex = kvp.Key; + var name = kvp.Value; + + // if a column already exists with the name append _i to the duplicates + var columnName = GetUniqueColumnName(result, name); + var column = new DataColumn(columnName, typeof(object)) { Caption = name }; + result.Columns.Add(column); + columnIndices.Add(columnIndex); } + } + else + { + for (var i = 0; i < self.FieldCount; i++) + { + if (configuration.FilterColumn != null && !configuration.FilterColumn(self, i)) + { + continue; + } - var name = configuration.UseHeaderRow - ? Convert.ToString(self.GetValue(i), CultureInfo.CurrentCulture) - : null; + var name = configuration.UseHeaderRow + ? Convert.ToString(self.GetValue(i), CultureInfo.CurrentCulture) + : null; - if (string.IsNullOrEmpty(name)) - { - name = configuration.EmptyColumnNamePrefix + i; - } + if (string.IsNullOrEmpty(name)) + { + name = configuration.EmptyColumnNamePrefix + i; + } - // if a column already exists with the name append _i to the duplicates - var columnName = GetUniqueColumnName(result, name); - var column = new DataColumn(columnName, typeof(object)) { Caption = name }; - result.Columns.Add(column); - columnIndices.Add(i); + // if a column already exists with the name append _i to the duplicates + var columnName = GetUniqueColumnName(result, name); + var column = new DataColumn(columnName, typeof(object)) { Caption = name }; + result.Columns.Add(column); + columnIndices.Add(i); + } } result.BeginLoadData(); diff --git a/src/ExcelDataReader.DataSet/ExcelDataTableConfiguration.cs b/src/ExcelDataReader.DataSet/ExcelDataTableConfiguration.cs index 4fe58463..0b22489a 100644 --- a/src/ExcelDataReader.DataSet/ExcelDataTableConfiguration.cs +++ b/src/ExcelDataReader.DataSet/ExcelDataTableConfiguration.cs @@ -1,4 +1,4 @@ -namespace ExcelDataReader +namespace ExcelDataReader { /// /// Processing configuration options and callbacks for AsDataTable(). @@ -20,6 +20,15 @@ public class ExcelDataTableConfiguration /// public Action ReadHeaderRow { get; set; } + /// + /// Gets or sets a callback to allow a custom implementation of header reading. + /// The returned dictionary will be used to construct the resulting DataTable. + /// Each element of the dictionary specifies an index and column name pair. + /// An example use of this would be to combine multiple header rows. + /// NOTE: If this field is set, UseHeaderRow, EmptyColumnNamePrefix, and FilterColumn are ignored. + /// + public Func> ReadHeader { get; set; } + /// /// Gets or sets a callback to determine whether to include the current row in the DataTable. /// diff --git a/test/ExcelDataReader.Tests/ExcelOpenXmlReaderTest.cs b/test/ExcelDataReader.Tests/ExcelOpenXmlReaderTest.cs index 0879349b..4c6d1731 100644 --- a/test/ExcelDataReader.Tests/ExcelOpenXmlReaderTest.cs +++ b/test/ExcelDataReader.Tests/ExcelOpenXmlReaderTest.cs @@ -388,5 +388,69 @@ public void GitIssue425() Assert.That(dataSet.Tables[0].Rows[1].ItemArray[0], Is.EqualTo("text")); Assert.That(dataSet.Tables[0].Rows[2].ItemArray[0], Is.EqualTo("text text")); } + + [Test] + public void GitIssue518MultipleHeaderRows() + { + using (var reader = OpenReader("Test_git_issue_518")) + { + var dataSet = reader.AsDataSet(new ExcelDataSetConfiguration() + { + ConfigureDataTable = _ => new ExcelDataTableConfiguration() + { + UseHeaderRow = true, + ReadHeader = self => + { + var headerNames = new List(); + + // read first header row + for (var i = 0; i < self.FieldCount; i++) + { + var s = Convert.ToString(self.GetValue(i)); + headerNames.Add(s); + } + + // append second header row + if (!self.Read()) { + throw new Exception(); + } + var result = new Dictionary(); + for (var i = 0; i < self.FieldCount; i++) + { + var first = headerNames[i]; + var second = Convert.ToString(self.GetValue(i)); + string name; + if (first.Length == 0) + { + name = second; + } + else if (second.Length == 0) + { + name = first; + } + else + { + name = first + " " + second; + } + if (string.IsNullOrEmpty(name)) + { + name = "Column" + i; + } + result.Add(i, name); + } + return result; + } + } + }); + + var columns = dataSet.Tables[0].Columns; + Assert.That(columns[0].ColumnName.ToString(), Is.EqualTo("ColName1 A")); + Assert.That(columns[1].ColumnName.ToString(), Is.EqualTo("ColName1 B")); + Assert.That(columns[2].ColumnName.ToString(), Is.EqualTo("ColName2 B")); + Assert.That(columns[3].ColumnName.ToString(), Is.EqualTo("FirstOnly")); + Assert.That(columns[4].ColumnName.ToString(), Is.EqualTo("SecondOnly")); + Assert.That(columns[5].ColumnName.ToString(), Is.EqualTo("Another One")); + } + } } } diff --git a/test/Resources/Test_git_issue_518.xlsx b/test/Resources/Test_git_issue_518.xlsx new file mode 100644 index 00000000..1e678c96 Binary files /dev/null and b/test/Resources/Test_git_issue_518.xlsx differ