diff --git a/src/NetCore.Utilities.Spreadsheet.Tests/OpenXmlSpreadsheetParserTests.cs b/src/NetCore.Utilities.Spreadsheet.Tests/OpenXmlSpreadsheetParserTests.cs index e9aff96..f245209 100644 --- a/src/NetCore.Utilities.Spreadsheet.Tests/OpenXmlSpreadsheetParserTests.cs +++ b/src/NetCore.Utilities.Spreadsheet.Tests/OpenXmlSpreadsheetParserTests.cs @@ -42,4 +42,23 @@ public void ParseDocument_ShouldReturnProperData() Assert.Equal("John Smith", firstRecord.Name); Assert.Equal(55, firstRecord.Age); } + + [Fact] + public void ParseDocument_ShouldReturnProperData_BySheetName() + { + //Arrange + var filePath = "../../../SampleFiles/ImportSample.xlsx"; + var expectedCount = 3; + + //Act + var result = _spreadsheetParser.ParseDocument(File.OpenRead(filePath), "Test Sheet", true); + + //Assert + Assert.Equal(expectedCount, result.Count); + var firstRecord = result.First(); + Assert.Equal("John Smith", firstRecord.Name); + Assert.Equal(55, firstRecord.Age); + var lastRecord = result.Last(); + Assert.Equal("Adam", lastRecord.Name); + } } \ No newline at end of file diff --git a/src/NetCore.Utilities.Spreadsheet.Tests/SampleExportRecord.cs b/src/NetCore.Utilities.Spreadsheet.Tests/SampleExportRecord.cs index 21e0c51..8531ad3 100644 --- a/src/NetCore.Utilities.Spreadsheet.Tests/SampleExportRecord.cs +++ b/src/NetCore.Utilities.Spreadsheet.Tests/SampleExportRecord.cs @@ -1,5 +1,6 @@ using System; using System.ComponentModel; +using System.ComponentModel.DataAnnotations; namespace ICG.NetCore.Utilities.Spreadsheet.Tests; @@ -99,7 +100,7 @@ public class DifferentTestExportRecord [SpreadsheetImportColumn(1)] public int Id { get; set; } - [SpreadsheetColumn(displayName: "Company")] + [Display(Name = "Testing This Out")] [SpreadsheetImportColumn(2)] public string Company { get; set; } = ""; diff --git a/src/NetCore.Utilities.Spreadsheet.Tests/SampleFiles/ImportSample.xlsx b/src/NetCore.Utilities.Spreadsheet.Tests/SampleFiles/ImportSample.xlsx index c2244ba..2a55699 100644 Binary files a/src/NetCore.Utilities.Spreadsheet.Tests/SampleFiles/ImportSample.xlsx and b/src/NetCore.Utilities.Spreadsheet.Tests/SampleFiles/ImportSample.xlsx differ diff --git a/src/NetCore.Utilities.Spreadsheet.Tests/TypeDiscovererTests.cs b/src/NetCore.Utilities.Spreadsheet.Tests/TypeDiscovererTests.cs index b8089cf..e7d91c1 100644 --- a/src/NetCore.Utilities.Spreadsheet.Tests/TypeDiscovererTests.cs +++ b/src/NetCore.Utilities.Spreadsheet.Tests/TypeDiscovererTests.cs @@ -1,5 +1,7 @@ using System.ComponentModel; +using System.ComponentModel.DataAnnotations; using System.Linq; +using Bogus.DataSets; using FluentAssertions; using Xunit; @@ -39,6 +41,16 @@ public void Sets_DisplayName_From_SpreadsheetColumn_Attribute() results.First().DisplayName.Should().Be("Some Prop Name"); } + [Fact] + public void Sets_DisplayName_From_Display_Attribute() + { + var results = TypeDiscoverer.GetProps(typeof(Sets_DisplayName_From_DisplayAttribute_Attribute_TestCase)); + + results.Should().HaveCount(1); + + results.First().DisplayName.Should().Be("Some Prop Name"); + } + [Fact] public void Property_Excluded_From_SpreadsheetIgnore_Attribute() { @@ -81,6 +93,12 @@ private class Sets_DisplayName_From_SpreadsheetColumn_Attribute_TestCase public string SomeProp { get; set; } } + private class Sets_DisplayName_From_DisplayAttribute_Attribute_TestCase + { + [Display(Name = "Some Prop Name")] + public string SomeProp { get; set; } + } + private class Property_Excluded_From_SpreadsheetColumn_Attribute_TestCase { [SpreadsheetColumn(ignore: true)] diff --git a/src/NetCore.Utilities.Spreadsheet/ISpreadsheetParser.cs b/src/NetCore.Utilities.Spreadsheet/ISpreadsheetParser.cs index 7d698b0..b410492 100644 --- a/src/NetCore.Utilities.Spreadsheet/ISpreadsheetParser.cs +++ b/src/NetCore.Utilities.Spreadsheet/ISpreadsheetParser.cs @@ -36,4 +36,14 @@ public interface ISpreadsheetParser /// If set to true will skip the first row of data as header information /// The parsed information List ParseDocument(Stream fileStream, int worksheetNumber, bool skipHeaderRow) where T : new(); + + /// + /// Parses the provided document and returns a list of T objects based on the input data, using the specific worksheet by name + /// + /// The type to use for importing + /// The contents of the Excel File (XLSX format + /// + /// If set to true will skip the first row of data as header information + /// + List ParseDocument(Stream fileStream, string worksheetName, bool skipHeaderRow) where T : new(); } \ No newline at end of file diff --git a/src/NetCore.Utilities.Spreadsheet/OpenXmlSpreadsheetParser.cs b/src/NetCore.Utilities.Spreadsheet/OpenXmlSpreadsheetParser.cs index d8ed1fa..759bc26 100644 --- a/src/NetCore.Utilities.Spreadsheet/OpenXmlSpreadsheetParser.cs +++ b/src/NetCore.Utilities.Spreadsheet/OpenXmlSpreadsheetParser.cs @@ -29,6 +29,18 @@ public class OpenXmlSpreadsheetParser : ISpreadsheetParser /// public List ParseDocument(Stream fileStream, int worksheetNumber, bool skipHeaderRow) where T : new() + { + return ParseDocumentInternal(fileStream, worksheetNumber, string.Empty, skipHeaderRow); + } + + /// + public List ParseDocument(Stream fileStream, string worksheetName, bool skipHeaderRow) where T : new() + { + return ParseDocumentInternal(fileStream, null, worksheetName, skipHeaderRow); + } + + private List ParseDocumentInternal(Stream fileStream, int? worksheetNumber, string worksheetName, + bool skipHeaderRow) where T : new() { //Validate object is properly created var importColumnDefinitions = typeof(T) @@ -49,13 +61,24 @@ public class OpenXmlSpreadsheetParser : ISpreadsheetParser var workbookPart = excelDoc.WorkbookPart; if (workbookPart == null) throw new SpreadsheetParserException("Spreadsheet has no WorkbookPart"); - var sheet = workbookPart.Workbook.Descendants().Skip(worksheetNumber - 1).FirstOrDefault(); - if (sheet == null) throw new SpreadsheetParserException($"Workbook does not have {worksheetNumber} sheets"); + Sheet sheet; + if (worksheetNumber.HasValue) + { + sheet = workbookPart.Workbook.Descendants().Skip(worksheetNumber.Value - 1).FirstOrDefault(); + if (sheet == null) throw new SpreadsheetParserException($"Workbook does not have {worksheetNumber} sheets"); + } + else + { + sheet = workbookPart.Workbook.Descendants().First(s => s.Name == worksheetName); + if (sheet == null) + throw new SpreadsheetParserException($"Workbook does not have a sheet named '{worksheetName}'"); + } + if (sheet.Id == null || !sheet.Id.HasValue || sheet.Id.Value == null) throw new SpreadsheetParserException($"Sheet {worksheetNumber} has a null Id"); - if (workbookPart.GetPartById(sheet.Id.Value) is not WorksheetPart wsPart) + if (workbookPart.GetPartById(sheet.Id.Value) is not WorksheetPart wsPart) throw new SpreadsheetParserException($"Sheet {worksheetNumber} with Id {sheet.Id.Value} is not in the workbook"); - + var collection = new Collection(); var skipRows = skipHeaderRow ? 1 : 0; var expectedColumns = importColumnDefinitions.Max(c => c.Column) - 1; @@ -86,8 +109,9 @@ public class OpenXmlSpreadsheetParser : ISpreadsheetParser return collection.ToList(); - } + + private static bool IsOfType(Type t) { var typeToCheck = typeof(T); diff --git a/src/NetCore.Utilities.Spreadsheet/TypeDiscoverer.cs b/src/NetCore.Utilities.Spreadsheet/TypeDiscoverer.cs index 87d5a08..0193fba 100644 --- a/src/NetCore.Utilities.Spreadsheet/TypeDiscoverer.cs +++ b/src/NetCore.Utilities.Spreadsheet/TypeDiscoverer.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.ComponentModel; +using System.ComponentModel.DataAnnotations; using System.Text.RegularExpressions; namespace ICG.NetCore.Utilities.Spreadsheet; @@ -48,6 +49,11 @@ public static IList GetProps(Type t) propName = sca.DisplayName ?? propName; width = sca.Width; } + else if (attr is DisplayAttribute display) + { + if (!string.IsNullOrEmpty(display.Name)) + propName = display.Name; + } } if (ignored) continue;