diff --git a/AlphaCSV/CSVParser.cs b/AlphaCSV/CSVParser.cs index c17f760..fd60133 100644 --- a/AlphaCSV/CSVParser.cs +++ b/AlphaCSV/CSVParser.cs @@ -163,6 +163,15 @@ public DataTable ParseDefinedCSV(DataTable schema, string path, CSVParseOptions return table; } + /// + /// Parses a CSV file containing values for the public set properties of the specified type + /// + /// The type that we will receive + /// The path of the CSV File + /// CSV Parsing options + /// Validation Patterns + /// + /// public List ParseType(string path, CSVParseOptions options = null, List validationPatterns = null) { Type genType = typeof(T); ConstructorInfo constructor = genType.GetConstructor(new Type[] { }); @@ -185,18 +194,26 @@ public List ParseType(string path, CSVParseOptions options = null, List result = new List(table.Rows.Count); foreach (DataRow row in table.Rows) { object GenericInstance = constructor.Invoke(null); - for (int i = 0; i < propertySetMethods.Count; i++) { - propertySetMethods[i].Invoke(GenericInstance, new object[] { row[i] }); + for (int i = 0; i < table.Columns.Count; i++) { + string name = table.Columns[i].ColumnName; + //We cannot be sure that the CSV field order is correct especially when dealing with derived classes. + //Thus we need to read the file first and then correlate the column name to the field of the class that we + //need to instantiate. + int indexToUse = propertNames.IndexOf(name); + if (indexToUse == -1) { + throw new InvalidOperationException($"There is no property with name {name}"); + } + object covertedValue = Convert.ChangeType(row[i], propertyTypes[indexToUse]); + propertySetMethods[indexToUse].Invoke(GenericInstance, new object[] { covertedValue }); } result.Add((T)GenericInstance); } diff --git a/TestAlphaCSV/CSVReaderTests.ClassParsing.cs b/TestAlphaCSV/CSVReaderTests.ClassParsing.cs index 291a9f9..a5f4557 100644 --- a/TestAlphaCSV/CSVReaderTests.ClassParsing.cs +++ b/TestAlphaCSV/CSVReaderTests.ClassParsing.cs @@ -15,6 +15,11 @@ public record TestRecord { public string Surname { get; set; } } + public record DerivedRecord : TestRecord { + public string FatherName { get; set; } + } + + [TestMethod] public void TestSimpleClassParsing() { @@ -39,6 +44,34 @@ public void TestSimpleClassParsing() { List actual = parser.ParseType(path); + //Assert + CollectionAssert.AreEqual(expected, actual); + } + + [TestMethod] + public void TestDerivedClass() { + string input = "Name,Surname,FatherName\nJohn,Doe,Donald\n"; + + //Arrange + MockFileSystem fs = new MockFileSystem(); + MockFileData mockInputFile = new MockFileData(input); + string path = @"C:\test.csv"; + fs.AddFile(path, mockInputFile); + + DerivedRecord rc = new DerivedRecord { + Name = "John", + Surname = "Doe", + FatherName = "Donald" + }; + List expected = new List { rc }; + + + //Act + CSVParser parser = new CSVParser(fs); //Inject dependency here + //Since we have the expected result we just clone the schema instead of building it by hand. + List actual = parser.ParseType(path); + + //Assert CollectionAssert.AreEqual(expected, actual); }