Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/search/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Powershell 使用递归将哈希表转换为对象_Powershell - Fatal编程技术网

Powershell 使用递归将哈希表转换为对象

Powershell 使用递归将哈希表转换为对象,powershell,Powershell,我有一个返回哈希表的函数,我需要将键转换成嵌套对象,但我正在失去理智 下面是我正在处理的哈希表的一个硬编码示例 # $hash = SomeFunctionThatReturnsAhashTable $hash = @{ 'root.Blubb' = @(5) 'root.controller.haha' = 'hoho', 'hoho' 'root.controller.hugo' = @(12) 'root.controller.name' = '10.3.

我有一个返回哈希表的函数,我需要将键转换成嵌套对象,但我正在失去理智

下面是我正在处理的哈希表的一个硬编码示例

# $hash = SomeFunctionThatReturnsAhashTable

$hash = @{
    'root.Blubb' = @(5)
    'root.controller.haha' = 'hoho', 'hoho'
    'root.controller.hugo' = @(12)
    'root.controller.name' = '10.3.3.171', '10.3.3.172'
    'root.controller.renate' = @(123)
    'root.controller.test' = 2, 2
    'root.controller.upsala' = @('handy')
    'root.t.t1.wert' = @(1)
    'root.t.t2.wert' = @(2)
    'root.test' = 1, 2
}
下面是我希望将哈希表转换成什么的想法

$obj = [pscustomobject]@{
    root = [pscustomobject]@{
        Blubb = @(5)
        controller = [pscustomobject]@{
            haha = 'hoho', 'hoho'
            hugo = @(12)
            name = '10.3.3.171', '10.3.3.172'
            renate = @(123)
            test = 2, 2
            upsala = @('handy')
        }
        t = [pscustomobject]@{
            t1 = [pscustomobject]@{
                wert = @(1)
            }
            t2 = [pscustomobject]@{
                wert = @(2)
            }
        }
        test = 1, 2
    }
}
我试图在“.”上拆分并返回子对象,但我不知道如何完成。如果有更好的办法,请告诉我。这就是我目前所拥有的

function keytoobject ($key, $value) {
    if ($key.contains('.')) {
        [pscustomobject]@{
            ($key.substring($key.indexof('.')+1)) = (keytoobject $key.substring($key.indexof('.')+1) $value)
        }
    } else {
        [pscustomobject]@{
            $key = $value
        }
    }
}

$hash.Keys | % {
    keytoobject $_ ($hash[$_])
}

任何帮助都将不胜感激。

我感觉这可以用一种更优雅的方式来完成,但这是我所能想到的(基于)

其思想是创建一个具有所有所需级别的哈希表树,然后将值插入它们应该位于的位置(在哈希表中比在对象中更容易实现),最后但并非最不重要的一点是:将哈希表强制转换为
PSCustomObject
。就像问题中的样品/图纸一样

#Don't mind the sexy function-name
function ConvertDelimitedHashtableTo-NestedObject ([hashtable]$Hash) {

    #Hashtable to store data in
    $result = @{}

    #iex = Invoke-Expression
    #It can execute a command stored in a string.
    #It's necessary because we don't know the path before runtime (since paths depends on the inputdata).

    #Design skeleton (get path to every "parent node"/hashtable/object)
    $paths = $hash.Keys |
    #Only "delimited" keys will require a hashtable/subobject (without this, $hash = @{ 'hello' = 'world' } would fail)
    Where-Object { $_ -match '\.' } | ForEach-Object { 
        #Split string into nodes
        $parts = $_.split(".")
        0..($parts.count -2) | Foreach-Object {
            #Get every node-path except deepest level (value-node/property)
            "`$result.$($parts[0..$_] -join '.')"
        }
    } |
    #Remove duplicates
    Select-Object -Unique |
    #Sort by number of levels (because we can't create root.t before root exists)
    Sort-Object {@($_.ToCharArray() -eq '.').Count}


    #Create skeleton
    $paths | ForEach-Object {
        #Creating hashtable for each level (except values-nodes) to get a complete skeleton/tree
        iex "$_ = @{}"
    }

    #Insert values
    $hash.Keys | ForEach-Object {
        #Add values/properties to the correct hashtable with value from the input-hashtable
        iex "`$result.$_ = `$hash['$_']"
    }

    #Convert each hashtable-node to PSCustomObject
    $paths | ForEach-Object {
        iex "$_ = [pscustomobject]$_"
    }

    #Output main-hashtable as PSCustomObject
    [pscustomobject]$result
} 


#Original object
$myht = @{
    'root.Blubb' = @(5)
    'root.controller.haha' = 'hoho', 'hoho'
    'root.controller.hugo' = @(12)
    'root.controller.name' = '10.3.3.171', '10.3.3.172'
    'root.controller.renate' = @(123)
    'root.controller.test' = 2, 2
    'root.controller.upsala' = @('handy')
    'root.t.t1.wert' = @(1)
    'root.t.t2.wert' = @(2)
    'root.test' = 1, 2
}

$obj = ConvertDelimitedHashtableTo-NestedObject -Hash $myht
这将生成并执行以下代码(我从脚本中删除了
iex
,因此它只输出生成的每一行代码):

并为您提供此对象(使用
格式自定义
显示整个树):


天哪!我已经为此工作了几个小时,但我想我有一些东西正在工作。我不得不使用
addmember
,但这是我创建空对象的方法,因此它们不等于
$null
。这很重要,因为需要创建使用新嵌套对象确定的测试

function Add-NestedObject($sourceObject, $path, $objectData){
    # This function will add the object $objectToNest into $sourceObject into the location named by $parentPath
    $currentPath,$remainingPath = $path.Split(".",2)

    # Check to see if the object contains the following subproperty.
    if($sourceObject.$currentPath -eq $null){
        # This property does not exist and needs to be created. Use an empty object
        Add-Member -Name $currentPath -TypeName PSObject -InputObject $sourceObject -MemberType NoteProperty -Value (New-Object -TypeName PSObject)
    }

    # Are there more elements to this path?
    if($remainingPath){
        # There are more nested objects. Keep passing data until we get to the point where we can populate it.  
        Add-NestedObject ($sourceObject.$currentPath) $remainingPath $objectData
    } else {
        # Now we can use the data and populate this object.
        $props = @{} 
        $objectData | ForEach-Object{
            $_.Name = $_.Name.Split(".")[-1]
            $props.($_.Name) = $_.Value
        }

        # Set the current path in the object to contain the data we have been passing. 
        $sourceObject.$currentPath = [pscustomobject]$props
    }
}


$schema = $hash.GetEnumerator() | 
    Select-Object Name,Value,@{Name="Parent";Expression={$split = $_.Name -split "\.";$split[0..($split.Count - 2)] -join "."}} | 
    Group-Object Parent | Sort-Object Name

# Empty Object to start
$object = New-Object -TypeName PSObject 

# Build the object skeleton
$schema | ForEach-Object{Add-NestedObject $object $_.Name $_.Group}

# Show the monstrosity
$object
最基本的是,我们使用组对象将所有值收集到父属性中。对于每个父属性,我们使用递归函数创建路径中的每个节点(假设它不存在)。一旦我们创建了所有节点,我们就可以将值集合放在该节点内

值集合将重建为自定义对象,并指定给结束节点

下面是JSON的外观,这样您就可以看到对象在转换后的外观

{
    "root":  {
                 "test":  [
                              1,
                              2
                          ],
                 "Blubb":  [
                               5
                           ],
                 "controller":  {
                                    "name":  [
                                                 "10.3.3.171",
                                                 "10.3.3.172"
                                             ],
                                    "haha":  [
                                                 "hoho",
                                                 "hoho"
                                             ],
                                    "hugo":  [
                                                 12
                                             ],
                                    "test":  [
                                                 2,
                                                 2
                                             ],
                                    "upsala":  [
                                                   "handy"
                                               ],
                                    "renate":  [
                                                   123
                                               ]
                                },
                 "t":  {
                           "t1":  {
                                      "wert":  [
                                                   1
                                               ]
                                  },
                           "t2":  {
                                      "wert":  [
                                                   2
                                               ]
                                  }
                       }
             }
}

下面是一种非常简单的递归嵌套哈希表方法:

#Example hash
$obj = @{A="B";c=@{D="F";g="H"}}


# the function
function Get-HashAsObject
{
    param ([hashtable]$hash, [switch]$Deep)

    $NewHash = @{}
    foreach ($k in $hash.Keys)
    {
        if ($hash[$k] -is [hashtable] -and $Deep)
        {
            $NewHash.Add($k,(Get-HashAsObject -Deep -hash $hash[$k]))
        }
        else
        {
            $NewHash.Add($k,$hash[$k])
        }
    }
    return [PSCustomObject]$NewHash
}

"Shallow"
$s = Get-HashAsObject $obj 
$s | fc
"Deep"
$d = Get-HashAsObject $obj -Deep
$d | fc
输出:

浅层

class PSCustomObject
{
  A = B
  c = 
    [
      class DictionaryEntry
      {
        Key = D
        Value = F
        Name = D
      }
      class DictionaryEntry
      {
        Key = g
        Value = H
        Name = g
      }
    ]

}
Deep

class PSCustomObject
{
  A = B
  c = 
    class PSCustomObject
    {
      D = F
      g = H
    }
}

在这个场景中,
root.controller
是否有一个值?我这样问是因为它可能是一个复杂的问题,应该解决如何处理它。期望的结果是root.controller的值将是一个具有6个属性的单个对象,如所提供代码的第二部分所示。我的意思是尽可能好的:“为什么有这样的数据格式”?废话。。。我几乎回答完了。希望它足够不同。干得好,谢谢。迫不及待地想看看你有什么想法。在我开始工作之前,我必须重新开始1,2…5次。:-)哦,我的上帝,这件事让我秃顶了。我刚刚完成了物体的骨架。现在只是填充它…显示这些类型的东西非常有用。尽管我在这里没有“赢”,我还是更新了我的答案。经过几个小时的睡眠后,我想我可以简化很多我很自豪,再也不想看到这一切了。祝贺你。我的前4次尝试是使用递归,但我找不到阻止它的方法。不确定我更喜欢哪种方法,但至少这更容易阅读。:-)我差点放弃这一点,更倾向于使用调用表达式。我想这是一般方法3第6版哇@弗罗德夫,马特,你们绝对,毫无疑问是令人惊奇的。如果我能标出两个答案,我会的。在看到这一切之后,我意识到我在这件事上远远超出了我的深度。我知道我只是互联网上的无名小卒,但说真的,这些都是最令人印象深刻的。我之所以选择这一个作为答案,主要是因为我个人觉得在powershell中使用iex和哈希表不太舒服。谢谢你们两位的知识,没问题。我现在把我的答案简化了一点,这样就不会那么难理解了
Invoke Expression
像普通用户一样运行命令,但由于我们还不知道要运行的代码(因为每次
$hash
可能不同),所以我们动态创建它。要理解代码的作用,只需从脚本中删除
iex
,所有行都将打印到屏幕上,以便您可以看到生成对象的实际脚本。您会注意到,它正是您在示例中描述的:使用值创建嵌套哈希表,然后将它们强制转换为
pscustomobject
:-)谢谢你的帮助!运行之后,我无法执行类似于
$d.root
的操作来查看该级别的所有属性。当我执行
$d|converttojson
时,输出与我看到的matt或frode的结果不匹配
class PSCustomObject
{
  A = B
  c = 
    [
      class DictionaryEntry
      {
        Key = D
        Value = F
        Name = D
      }
      class DictionaryEntry
      {
        Key = g
        Value = H
        Name = g
      }
    ]

}
class PSCustomObject
{
  A = B
  c = 
    class PSCustomObject
    {
      D = F
      g = H
    }
}