diff --git a/Examples/Excel/Import/Example-ImportDataTable.ps1 b/Examples/Excel/Import/Example-ImportDataTable.ps1 new file mode 100644 index 0000000..2e4e3ed --- /dev/null +++ b/Examples/Excel/Import/Example-ImportDataTable.ps1 @@ -0,0 +1,13 @@ +Import-Module ../../PSWriteOffice.psd1 -Force + +$path = "$PSScriptRoot/DataTable.xlsx" +$workbook = New-OfficeExcel +$sheet = New-OfficeExcelWorkSheet -Workbook $workbook -WorksheetName 'Data' -Option Replace +New-OfficeExcelValue -Worksheet $sheet -Row 1 -Column 1 -Value 'Name' +New-OfficeExcelValue -Worksheet $sheet -Row 1 -Column 2 -Value 'Age' +New-OfficeExcelValue -Worksheet $sheet -Row 2 -Column 1 -Value 'Jane' +New-OfficeExcelValue -Worksheet $sheet -Row 2 -Column 2 -Value 31 +Save-OfficeExcel -Workbook $workbook -FilePath $path + +$table = Import-OfficeExcel -FilePath $path -AsDataTable +$table | Format-Table diff --git a/Examples/Excel/Import/Example-ImportTyped.ps1 b/Examples/Excel/Import/Example-ImportTyped.ps1 new file mode 100644 index 0000000..661356f --- /dev/null +++ b/Examples/Excel/Import/Example-ImportTyped.ps1 @@ -0,0 +1,18 @@ +Import-Module ../../PSWriteOffice.psd1 -Force + +class Person { + [string]$Name + [int]$Age +} + +$path = "$PSScriptRoot/Typed.xlsx" +$workbook = New-OfficeExcel +$sheet = New-OfficeExcelWorkSheet -Workbook $workbook -WorksheetName 'Data' -Option Replace +New-OfficeExcelValue -Worksheet $sheet -Row 1 -Column 1 -Value 'Name' +New-OfficeExcelValue -Worksheet $sheet -Row 1 -Column 2 -Value 'Age' +New-OfficeExcelValue -Worksheet $sheet -Row 2 -Column 1 -Value 'John' +New-OfficeExcelValue -Worksheet $sheet -Row 2 -Column 2 -Value 30 +Save-OfficeExcel -Workbook $workbook -FilePath $path + +$rows = Import-OfficeExcel -FilePath $path -Type ([Person]) +$rows | Format-Table diff --git a/Sources/PSWriteOffice/Cmdlets/Excel/ImportOfficeExcelCommand.cs b/Sources/PSWriteOffice/Cmdlets/Excel/ImportOfficeExcelCommand.cs index 6e008e5..252b3dd 100644 --- a/Sources/PSWriteOffice/Cmdlets/Excel/ImportOfficeExcelCommand.cs +++ b/Sources/PSWriteOffice/Cmdlets/Excel/ImportOfficeExcelCommand.cs @@ -42,22 +42,34 @@ public class ImportOfficeExcelCommand : PSCmdlet [Parameter] public SwitchParameter NoHeader { get; set; } + [Parameter] + public Type? Type { get; set; } + + [Parameter] + public SwitchParameter AsDataTable { get; set; } + protected override void ProcessRecord() { try { - var data = ExcelDocumentService.ImportWorkbook(FilePath, WorkSheetName, Culture, StartRow, EndRow, StartColumn, EndColumn, HeaderRow, NoHeader); - if (WorkSheetName != null && WorkSheetName.Length == 1 && data.TryGetValue(WorkSheetName[0], out var single)) + var raw = ExcelDocumentService.ImportWorkbook(FilePath, WorkSheetName, Culture, StartRow, EndRow, StartColumn, EndColumn, HeaderRow, NoHeader); + var converted = new Dictionary(); + foreach (var kvp in raw) + { + converted[kvp.Key] = ExcelDocumentService.ConvertWorksheetData(kvp.Value, Type, AsDataTable); + } + + if (WorkSheetName != null && WorkSheetName.Length == 1 && converted.TryGetValue(WorkSheetName[0], out var single)) { - WriteObject(single, true); + WriteObject(single, !AsDataTable.IsPresent); } - else if ((WorkSheetName == null || WorkSheetName.Length == 0) && data.Count == 1) + else if ((WorkSheetName == null || WorkSheetName.Length == 0) && converted.Count == 1) { - WriteObject(data.Values.First(), true); + WriteObject(converted.Values.First(), !AsDataTable.IsPresent); } else { - WriteObject(data); + WriteObject(converted); } } catch (FileNotFoundException ex) diff --git a/Sources/PSWriteOffice/Services/Excel/ExcelDocumentService.Worksheet.Data.cs b/Sources/PSWriteOffice/Services/Excel/ExcelDocumentService.Worksheet.Data.cs index 2342d80..6eec5ae 100644 --- a/Sources/PSWriteOffice/Services/Excel/ExcelDocumentService.Worksheet.Data.cs +++ b/Sources/PSWriteOffice/Services/Excel/ExcelDocumentService.Worksheet.Data.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; +using System.Data; using System.Globalization; +using System.Linq; using ClosedXML.Excel; namespace PSWriteOffice.Services.Excel; @@ -87,4 +89,74 @@ public static partial class ExcelDocumentService yield return rowData; } } + + private static IEnumerable MapRowsToType(IEnumerable> rows, Type type) + { + var properties = type.GetProperties().Where(p => p.CanWrite).ToArray(); + foreach (var row in rows) + { + var instance = Activator.CreateInstance(type); + if (instance == null) + { + continue; + } + + foreach (var property in properties) + { + var matchingKey = row.Keys.FirstOrDefault(k => string.Equals(k, property.Name, StringComparison.OrdinalIgnoreCase)); + if (matchingKey == null) + { + continue; + } + + var value = row[matchingKey]; + if (value == null) + { + property.SetValue(instance, null); + continue; + } + + try + { + var targetType = Nullable.GetUnderlyingType(property.PropertyType) ?? property.PropertyType; + var converted = Convert.ChangeType(value, targetType, CultureInfo.InvariantCulture); + property.SetValue(instance, converted); + } + catch + { + // ignore conversion failures + } + } + + yield return instance; + } + } + + private static DataTable BuildDataTable(IEnumerable> rows) + { + var table = new DataTable(); + var columnsAdded = false; + + foreach (var row in rows) + { + if (!columnsAdded) + { + foreach (var key in row.Keys) + { + table.Columns.Add(key, typeof(object)); + } + + columnsAdded = true; + } + + var dataRow = table.NewRow(); + foreach (var key in row.Keys) + { + dataRow[key] = row[key] ?? DBNull.Value; + } + table.Rows.Add(dataRow); + } + + return table; + } } diff --git a/Sources/PSWriteOffice/Services/Excel/ExcelDocumentService.cs b/Sources/PSWriteOffice/Services/Excel/ExcelDocumentService.cs index 09450c4..3e605b0 100644 --- a/Sources/PSWriteOffice/Services/Excel/ExcelDocumentService.cs +++ b/Sources/PSWriteOffice/Services/Excel/ExcelDocumentService.cs @@ -1,5 +1,14 @@ +using System; +using System.Collections.Generic; + namespace PSWriteOffice.Services.Excel; public static partial class ExcelDocumentService { + public static object ConvertWorksheetData(IEnumerable> rows, Type? type, bool asDataTable) + => asDataTable + ? BuildDataTable(rows) + : type != null + ? MapRowsToType(rows, type) + : rows; } diff --git a/Tests/ImportOfficeExcel.Tests.ps1 b/Tests/ImportOfficeExcel.Tests.ps1 index 4f6b734..5e27d40 100644 --- a/Tests/ImportOfficeExcel.Tests.ps1 +++ b/Tests/ImportOfficeExcel.Tests.ps1 @@ -80,6 +80,43 @@ Describe 'Import-OfficeExcel cmdlet' { $rows[0].Column2 | Should -Be 30 } + It 'imports data as specified type' { + class Person { + [string]$Name + [int]$Age + } + + $path = Join-Path $TestDrive 'typed.xlsx' + New-Item -Path $path -ItemType File | Out-Null + $workbook = New-OfficeExcel + $sheet1 = New-OfficeExcelWorkSheet -Workbook $workbook -WorksheetName 'Data' -Option Replace + New-OfficeExcelValue -Worksheet $sheet1 -Row 1 -Column 1 -Value 'Name' + New-OfficeExcelValue -Worksheet $sheet1 -Row 1 -Column 2 -Value 'Age' + New-OfficeExcelValue -Worksheet $sheet1 -Row 2 -Column 1 -Value 'John' + New-OfficeExcelValue -Worksheet $sheet1 -Row 2 -Column 2 -Value 30 + Save-OfficeExcel -Workbook $workbook -FilePath $path + $rows = Import-OfficeExcel -FilePath $path -Type ([Person]) + $rows[0].GetType().FullName | Should -Be ([Person]).FullName + $rows[0].Name | Should -Be 'John' + $rows[0].Age | Should -Be 30 + } + + It 'imports data as DataTable' { + $path = Join-Path $TestDrive 'datatable.xlsx' + New-Item -Path $path -ItemType File | Out-Null + $workbook = New-OfficeExcel + $sheet1 = New-OfficeExcelWorkSheet -Workbook $workbook -WorksheetName 'Data' -Option Replace + New-OfficeExcelValue -Worksheet $sheet1 -Row 1 -Column 1 -Value 'Name' + New-OfficeExcelValue -Worksheet $sheet1 -Row 1 -Column 2 -Value 'Age' + New-OfficeExcelValue -Worksheet $sheet1 -Row 2 -Column 1 -Value 'Jane' + New-OfficeExcelValue -Worksheet $sheet1 -Row 2 -Column 2 -Value 31 + Save-OfficeExcel -Workbook $workbook -FilePath $path + $table = Import-OfficeExcel -FilePath $path -AsDataTable + $table.GetType().FullName | Should -Be ([System.Data.DataTable]).FullName + $table.Rows.Count | Should -Be 1 + $table.Rows[0].Name | Should -Be 'Jane' + } + It 'throws for invalid path' { { Import-OfficeExcel -FilePath (Join-Path $TestDrive 'missing.xlsx') } | Should -Throw }