可以从变量创建PS自定义对象吗?

时间:2019-12-01 21:16:23

标签: arrays powershell pscustomobject

我在sql server中有一个100列的表,我想这样做,因此并不是所有的列都需要在文件中传递来加载。我已经在表中分配了列名,然后该表将比较哈希表中的列以查找匹配的列。然后,我根据要用于从文件插入数据的数组的匹配关系创建代码。问题是,它不喜欢调用一个变量来创建自定义对象。

我将以下内容存储在数组中。 (最多100个,以下为示例(例如,略过sqlcolumn2))。

  • sqlcolumn1 = if ([string]::IsNullOrEmpty($obj.P1) -eq $true) {$null} else {"$obj.P1"}
  • sqlcolumn3 = if ([string]::IsNullOrEmpty($obj.P2) -eq $true) {$null} else {"$obj.P2"}
  • sqlcolumn4 = if ([string]::IsNullOrEmpty($obj.P3) -eq $true) {$null} else {"$obj.P3"}
  • sqlcolumn5 = if ([string]::IsNullOrEmpty($obj.P4) -eq $true) {$null} else {"$obj.P4"}

这是数组:

foreach($line in $Final)
{
    $DataRow = "$($line."TableColumnName") = if ([string]::IsNullOrEmpty(`$obj.$($line."PName")) -eq `$true) {`$null} else {`"`$obj.$($line."PName")`"}"
    $DataArray += $DataRow
}

然后,我尝试将其添加到最终数组中,在该数组中,我希望对每行数据进行遍历,然后从该数组执行插入。即使上面的数组中的“字符串”值是经过手工编码的,也是正确的,但我无法识别它并运行。

foreach ($obj in $data2)
{
   $test = [PSCustomObject] @{  
   $DataArray = Invoke-Expression $DataArray
}

如果我只是键入$DataArray,它会不喜欢这样,因为它想要将我已经内置在字符串中的=符号。

这就是我正在尝试做的可能。

我试图以各种不同的方式来模板化我们接收此数据的方式,其中有些人向我们发送了100列中的30列,其他人或多或少地向我们发送了这些信息,没有人使用确切的列来缩减所有内容的单个脚本。

添加更多代码:

Function ArrayCompare() {
 [CmdletBinding()]
 PARAM(
    [Parameter(Mandatory=$True)]$Array1,
    [Parameter(Mandatory=$True)]$A1Match,
    [Parameter(Mandatory=$True)]$Array2,
    [Parameter(Mandatory=$True)]$A2Match)

 $Hash = @{}
 foreach ($Data In $Array1) {
    $Hash[$Data.$A1Match] += ,$Data
 }
 foreach ($Data In $Array2) {
    $Hash[$Data.$A2Match] += ,$Data
 }
 foreach ($KeyValue In $Hash.GetEnumerator()){

    $Match1, $Match2 = $KeyValue.Value.Where( {$_.$A1Match}, 'Split')

        [PSCustomObject]@{
        MatchValue = $KeyValue.Key
        A1Matches = $Match1.Count
        A2Matches = $Match2.Count
        TablePosition = [int]$Match2.TablePosition
        TableColumnName = $Match2.TableColumnName
        #  PName is the P(##) that is a generic ascending column value back to import-excel module.  ColumnA = P1, ColumnB = P2  etc..until no data is detected.  Allows flexibility and not having to know how many columns there are
        PName = $Match1.Name}  
 }
}


$Server = 'ServerName'
$Catalog = 'DBName'
$DestinationTable = 'ImportIntoTableName'

$FileIdentifierID = 10
$FileName = 'Test.xlsx'
$FilePath = 'C:\'

$FullFilePath =  $FilePath + $FileName
$data = Import-Excel -Path $FullFilePath -NoHeader -StartRow 1 # Import- 
Excel Module for working with xlsx excel files
$data2 = Import-Excel -Path $ullFilePath -NoHeader -StartRow 2 # Import- 
Excel Module for working with xlsx excel files

$ExpectedHeaderArray = @()
$HeaderArray = @()
$DataArray = @()
$HeaderDetect = @()

$HeaderDetect = $data | Select-Object -First 1 # Header Row In File

$HeaderDetect | 
ForEach-Object  {
                 $ColumnValue = $_
                 $ColumnValue | 
                 Get-Member -MemberType *Property |
                 Select-Object -ExpandProperty Name |
                 ForEach-Object  {
                                    $HeaderValues = [PSCustomObject]@{
                                    Name = $_
                                    Value = $ColumnValue.$_}
                                    $HeaderArray += $HeaderValues
                                 }
                 } 

# Query below provides a list of all expected file headers and the table 
column name they map to
$Query = "SELECT TableColumnName, FileHeaderName, TablePosition FROM 
dbo.FileHeaders WHERE FileIdentifierID = $($FileIdentifierID)" 
$ds = Invoke-Sqlcmd -ServerInstance $Server -Database $Catalog -Query $Query 
-OutputAs DataSet

$ExpectedHeaderArray =  foreach($Row in $ds.Tables[0].Rows)
    {
    new-object psObject -Property @{ 
        TableColumnName = "$($row.TableColumnName)"
        FileHeaderName = "$($row.FileHeaderName)"
        TablePosition = "$($row.TablePosition)"
    }
}

#Use Function Above
#Bring it together so we know what P(##) goes with which header in file/mapped to table column name
$Result = ArrayCompare -Array1 $HeaderArray -A1Match Value -Array2 $ExpectedHeaderArray -A2Match FileHeaderName  

$Final = $Result | sort TablePosition

foreach($Line in $Final)
{
    $DataRow = "$($Line."TableColumnName") = if ([string]::IsNullOrEmpty(`$obj.$($Line."PName")) -eq `$true) {`$null} else {`"`$obj.$($Line."PName"))`"}"
    $DataArray += $DataRow
}

# The output below is what the code inside the last array would be that I would use to import into excel. 
# The goal is to be dynamic and match headers in the file to the stored header value and import into a table (mapped from header column to table column name)
# The reason for this is before I was here, there were many different "versions" of a layout that was given out.  In the end, it is all one in the same
#    but some send all 100 columns, some only send a handful, some send 80 etc.  I am trying to have everything flow through here vs. 60+ pieces of code/stored procedures/ssis packs


 Write-Output $DataArray    

# Output Sample  -- Note how in the sample, P2 and subsequent skip SQLColumn2 because P2 maps to the header value of position 3 in the sql table and each after is one off.  
# In this example, SqlColumn2 would not be populated

# SqlColumn1 = if ([string]::IsNullOrEmpty($obj.P1) -eq $true) {$null} else {"$obj.P1"}
# SqlColumn3 = if ([string]::IsNullOrEmpty($obj.P2) -eq $true) {$null} else {"$obj.P2"}
# SqlColumn4 = if ([string]::IsNullOrEmpty($obj.P3) -eq $true) {$null} else {"$obj.P3"}
# SqlColumn5 = if ([string]::IsNullOrEmpty($obj.P4) -eq $true) {$null} else {"$obj.P4"}


# I know this doesn't work.  This is where I'm stuck, how to build an array now off of this output from above
foreach ($obj in $data2)
{
   $test = [PSCustomObject] @{  
   $DataArray = Invoke-Expression $DataArray}
}

1 个答案:

答案 0 :(得分:1)

我很想首先重申您的问题,只是为了确保我理解正确(可能我不明白!)...

  • 您有一个如下所示的excel文件:
+---+---------+---------+---------+
|   |    A    |    B    |    C    |
+---+---------+---------+---------+
| 1 | HeaderA | HeaderB | HeaderC |
+---+---------+---------+---------+
| 2 | Value P | Value Q | Value R |
+---+---------+---------+---------+
| 3 | Value S | Value T | Value U |
+---+---------+---------+---------+
  • 您还有一个数据库表,如下所示:
+---------+---------+---------+---------+
+ ColumnW | ColumnX | ColumnY | ColumnZ |
+---------+---------+---------+---------+
+ ....... | ....... | ....... | ....... |
+---------+---------+---------+---------+
  • 和类似这样的列映射表(注意,在此示例中未映射ColumnX
+-----------------+----------------+---------------+
| TableColumnName | FileHeaderName | TablePosition |
+-----------------+----------------+---------------+
|    ColumnW      |    HeaderA     |       1       |
+-----------------+----------------+---------------+
|    ColumnY      |    HeaderB     |       2       |
+-----------------+----------------+---------------+
|    ColumnZ      |    HeaderC     |       3       |
+-----------------+----------------+---------------+
  • 您要使用映射表中的数据将电子表格中的值插入数据库表中,从而获得以下信息:
+---------+---------+---------+---------+
+ ColumnW | ColumnX | ColumnY | ColumnZ |
+---------+---------+---------+---------+
+ Value P |   null  | Value Q | Value R |
+---------+---------+---------+---------+
+ Value S |   null  | Value T | Value U |
+---------+---------+---------+---------+

因此,让我们加载电子表格(这次让标题行生成有意义的属性名称):

$data = Import-Excel -Path ".\MySpreadsheet.xlsx";
write-host ($data | ft | out-string);

# HeaderA HeaderB HeaderC
# ------- ------- -------
# Value P Value Q Value R
# Value S Value T Value U

并获取您的列映射数据(我正在以编程方式创建内存中的数据集,但显然您是从数据库中读取的):

$mappings = new-object System.Data.DataTable;
$null = $mappings.Columns.Add("TableColumnName", [string]);
$null = $mappings.Columns.Add("FileHeaderName", [string]);
$null = $mappings.Columns.Add("TablePosition", [int]);
@(
    @{ "TableColumnName"="ColumnW"; "FileHeaderName"="HeaderA"; "TablePosition"=1 },
    @{ "TableColumnName"="ColumnY"; "FileHeaderName"="HeaderB"; "TablePosition"=2 },
    @{ "TableColumnName"="ColumnZ"; "FileHeaderName"="HeaderC"; "TablePosition"=3 }
) | % {
    $row = $mappings.NewRow();
    $row.TableColumnName = $_.TableColumnName;
    $row.FileHeaderName = $_.FileHeaderName;
    $row.TablePosition = $_.TablePosition;
    $mappings.Rows.Add($row);
}
$ds = new-object System.Data.DataSet;
$ds.Tables.Add($mappings);
write-host ($ds.Tables[0] | ft | out-string)

# TableColumnName FileHeaderName TablePosition
# --------------- -------------- -------------
# ColumnW         HeaderA                    1
# ColumnY         HeaderB                    2
# ColumnZ         HeaderC                    3

现在我们可以构建“映射”对象:

$values = @();
foreach( $row in $data )
{
    $properties = [ordered] @{};
    foreach( $mapping in $mappings )
    {
        $properties.Add($mapping.TableColumnName, $row."$($mapping.FileHeaderName)");
    }
    $values += new-object PSCustomObject -Property $properties;
}
write-host ($values | ft | out-string)

# ColumnW ColumnY ColumnZ
# ------- ------- -------
# Value P Value Q Value R
# Value S Value T Value U

棘手的位是$properties.Add($mapping.TableColumnName, $row."$($mapping.FileHeaderName)");-基本上,您可以在PowerShell中使用点字符串或变量来访问对象属性(我不确定确切的功能名称)-例如

PS> $myValue = new-object PSCustomObject -Property @{ "aaa"="bbb"; "ccc"="ddd" }
PS> $myValue."aaa"
bbb

PS> $myProperty = "aaa"
PS> $myValue.$myProperty
"bbb"

所以$row."$($mapping.FileHeaderName)"是一个表达式,其值等于$row中名为{em> 的$mapping.FileHeaderName属性的值。

最后,您可以使用现有过程将对象插入数据库中...

请注意,我无法完全确定ArrayCompare的实际作用,因此上述方法可能无法100%解决您的问题,但是希望它足够接近,您可以自己解决差异,也可以留下注释与您所需解决方案不同的地方。

希望这会有所帮助。