Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions Examples/Excel/Import/Example-ImportDataTable.ps1
Original file line number Diff line number Diff line change
@@ -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
18 changes: 18 additions & 0 deletions Examples/Excel/Import/Example-ImportTyped.ps1
Original file line number Diff line number Diff line change
@@ -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
24 changes: 18 additions & 6 deletions Sources/PSWriteOffice/Cmdlets/Excel/ImportOfficeExcelCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, object>();
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)
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -87,4 +89,74 @@ public static partial class ExcelDocumentService
yield return rowData;
}
}

private static IEnumerable<object> MapRowsToType(IEnumerable<IDictionary<string, object?>> 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<IDictionary<string, object?>> 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;
}
}
9 changes: 9 additions & 0 deletions Sources/PSWriteOffice/Services/Excel/ExcelDocumentService.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
using System;
using System.Collections.Generic;

namespace PSWriteOffice.Services.Excel;

public static partial class ExcelDocumentService
{
public static object ConvertWorksheetData(IEnumerable<IDictionary<string, object?>> rows, Type? type, bool asDataTable)
=> asDataTable
? BuildDataTable(rows)
: type != null
? MapRowsToType(rows, type)
: rows;
}
37 changes: 37 additions & 0 deletions Tests/ImportOfficeExcel.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
Loading