如何将powershell哈希表转换为对象



PowerShell中的一些哈希表,例如使用Import-PowerShellDataFile导入的哈希表,如果改为PSCustomObject,则会更容易导航。

@{
AllNodes = @(
@{
NodeName = 'SRV1'
Role = 'Application'
RunCentralAdmin = $true
},
@{
NodeName = 'SRV2'
Role = 'DistributedCache'
RunCentralAdmin = $true
},
@{
NodeName = 'SRV3'
Role = 'WebFrontEnd'
PSDscAllowDomainUser = $true
PSDscAllowPlainTextPassword = $true
CertificateFolder = '\mediasrvMedia'
},
@{
NodeName = 'SRV4'
Role = 'Search'
},
@{
NodeName = '*'
DatabaseServer = 'sql1'
FarmConfigDatabaseName = '__FarmConfig'
FarmContentDatabaseName = '__FarmContent'
CentralAdministrationPort = 1234
RunCentralAdmin = $false
}
);
NonNodeData = @{
Comment = 'No comment'
}
}

导入时,它将成为哈希表的哈希表

$psdnode = Import-PowerShellDataFile .nodefile.psd1
$psdnode
Name                           Value
----                           -----
AllNodes                       {System.Collections.Hashtable, System.Collect...
NonNodeData                    {Comment}
$psdnode.GetType()
IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Hashtable                                System.Object

当按属性名称导航时,数据结构会很奇怪。

现有答案中有很好的信息,但鉴于您的问题的一般标题,让我尝试系统概述

  • 您不需要将哈希表转换为[pscustomobject]实例,以便使用点表示法深入到其条目(属性(,如评论中所讨论的和iRon的回答中所示。

    • 一个简单的例子:

      @{ top = @{ nested = 'foo' } }.top.nested  # -> 'foo'
      
    • 有关详细信息,请参阅此答案。

  • 事实上,在可能的情况下,使用哈希表比[pscustomobject]s更可取,因为:

    • 它们比[pscustomobject]实例更轻(使用更少的内存(
    • 迭代构建和修改它们更容易

注意:

  • 以上内容不仅适用于[hashtable]类型,而且更普遍地适用于实现[System.Collections.IDictionary]接口或其通用对应System.Collections.Generic.IDictionary[TKey, TValue]]的类型实例,特别是包括有序哈希表(它们是System.String类型的实例,PowerShell允许您使用语法糖[ordered] @{ ... }构建(。

  • 除非另有说明,以下部分中的哈希表指的是所有此类类型。


如果需要将[hasthable]转换为[pscustomobject]

虽然许多标准cmdlet可以将[hasthable]s[pscustomobjects]s互换,但有些cmdlet不接受,尤其是ConvertTo-CsvExport-Csv(请参阅GitHub第10999号发布的更改功能请求(;在这种情况下,必须转换为[pscustomobject]

Cavat:Hastables可以拥有任何类型的密钥,而转换为[pscustomobject]总是需要使用字符串";键";,即属性名称。因此,并不是所有的哈希表都可以忠实地或有意义地转换为[pscustomobject]s。

  • 非嵌套哈希表转换为[pscustomobject]:

    • PowerShell为[pscustomobject]文本(例如[pscustomobject] @{ foo = 'bar'; baz = 42 }(提供的语法糖也通过预先存在的哈希工作;例如:

      $hash = @{ foo = 'bar'; baz = 42 } 
      $custObj = [pscustomobject] $hash   # Simply cast to [pscustomobject]
      
  • 嵌套哈希表,即对象转换为[pscustomobject]图:

    • 一个简单但有限且可能昂贵的解决方案如您自己的答案所示:使用ConvertTo-Json将哈希表转换为JSON,然后使用ConvertFrom-Json将生成的JSON重新转换为[pscustomobject]

      • 抛开性能不谈,这种方法的基本限制是,考虑到JSON只支持少数数据类型,类型保真度可能会丢失。虽然通过Import-PowerShellDataFile读取的哈希表并不重要,但给定的哈希表可能包含在JSON中没有意义表示的类型的实例
    • 您可以使用自定义转换函数ConvertFrom-HashTable(下面的源代码(来克服这一限制;例如(用Format-Custom -InputObject $custObj检查结果(:

      $hash = @{ foo = 'bar'; baz = @{ quux = 42 } } # nested hashtable
      $custObj = $hash | ConvertFrom-HashTable # convert to [pscustomobject] graph
      

ConvertFrom-HashTable源代码:

注意:尽管有这个名称,该函数通常支持实现IDictionary作为输入的类型的实例。

function ConvertFrom-HashTable {
param(
[Parameter(Mandatory, ValueFromPipeline)]
[System.Collections.IDictionary] $HashTable
)
process {
$oht = [ordered] @{} # Aux. ordered hashtable for collecting property values.
foreach ($entry in $HashTable.GetEnumerator()) {
if ($entry.Value -is [System.Collections.IDictionary]) { # Nested dictionary? Recurse.
$oht[$entry.Key] = ConvertFrom-HashTable -HashTable $entry.Value
} else { # Copy value as-is.
$oht[$entry.Key] = $entry.Value
}
}
[pscustomobject] $oht # Convert to [pscustomobject] and output.
}
}

问题是什么

@'
@{
AllNodes = @(
@{
NodeName = 'SRV1'
Role = 'Application'
RunCentralAdmin = $true
},
@{
NodeName = 'SRV2'
Role = 'DistributedCache'
RunCentralAdmin = $true
},
@{
NodeName = 'SRV3'
Role = 'WebFrontEnd'
PSDscAllowDomainUser = $true
PSDscAllowPlainTextPassword = $true
CertificateFolder = '\mediasrvMedia'
},
@{
NodeName = 'SRV4'
Role = 'Search'
},
@{
NodeName = '*'
DatabaseServer = 'sql1'
FarmConfigDatabaseName = '__FarmConfig'
FarmContentDatabaseName = '__FarmContent'
CentralAdministrationPort = 1234
RunCentralAdmin = $false
}
);
NonNodeData = @{
Comment = 'No comment'
}
}
'@ |Set-Content .nodes.psd1
$psdnode = Import-PowerShellDataFile .nodefile.psd1
$psdnode
Name                           Value
----                           -----
NonNodeData                    {Comment}
AllNodes                       {SRV1, SRV2, SRV3, SRV4…}
$psdnode.AllNodes.where{ $_.NodeName -eq 'SRV3' }.Role
WebFrontEnd

我昨天刚刚发现的一个非常简单的方法是:;双重转换";通过JSON。

$nodes = Import-PowerShellDataFile .nodes.psd1 | ConvertTo-Json | ConvertFrom-Json
$nodes
AllNodes
--------
{@{NodeName=SRV1; RunCentralAdmin=True; Role=Application}, @{NodeName=SRV2; RunCentralAdm...}
$nodes.GetType()   
IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     False    PSCustomObject                           System.Object

最新更新