通过Powershell中的变量解析/访问嵌套JSON/哈希表数据时出现问题

通过Powershell中的变量解析/访问嵌套JSON/哈希表数据时出现问题,json,powershell,Json,Powershell,我正在尝试通过Powershell动态解析和构建一些传入JSON文件的数据结构(将以非标准结构提供),然后处理这些文件中的数据并将它们移交给下一步 作为其中的一部分,我正试图将JSON文件的数据结构构建成一个基本的数据路径列表,供我解析&从中提取数据,以便我能够处理数组、嵌套JSON对象等等。到目前为止还不错 我陷入某种Powershell特性的地方是通过变量处理2+级的深度。让我给你一个很好的代码块来演示这个问题 # Generate a Quick JSON file with differ

我正在尝试通过Powershell动态解析和构建一些传入JSON文件的数据结构(将以非标准结构提供),然后处理这些文件中的数据并将它们移交给下一步

作为其中的一部分,我正试图将JSON文件的数据结构构建成一个基本的数据路径列表,供我解析&从中提取数据,以便我能够处理数组、嵌套JSON对象等等。到目前为止还不错

我陷入某种Powershell特性的地方是通过变量处理2+级的深度。让我给你一个很好的代码块来演示这个问题

# Generate a Quick JSON file with different data types & levels
[object]$QuickJson = @'
{
    "Name" : "I am a JSON",
    "Version" : "1.2.3.4",
    "SomeBool" : true,
    "NULLValue" : null,
    "ArrayOfVersions" : [1.0,2.0,3.0],
    "MyInteger" : 69,
    "NestedJSON" : {
        "Version" : 5.0,
        "IsReady" : false
    },
    "DoubleNestedJSON" : {
        "FirstLevel" : 1,
        "DataValue" : "I am at first nested JSON level!",
        "Second_JSON_Level" : {
            "SecondLevel" : 2,
            "SecondDataValue" : "I am on the 2nd nested level"
        }
    }
}
'@

# Import our JSON file into Powershell
[object]$MyPSJson = ConvertFrom-Json -InputObject $QuickJson
# Two quick string variables to access our JSON data paths
[string]$ShortJsonPath = "Name"
[string]$NestedJsonPath = "NestedJson.Version"
# Long string to access a double-nested JSON object
[string]$LongNestedJsonPath = "DoubleNestedJSON.Second_JSON_Level.SecondDataValue"

# Both of these work fine
Write-Host ("JSON Name (Direct) ==> " + $MyPSJson.Name)
Write-Host ("JSON Name (via Variable) ==> " + $MyPSJson.$ShortJsonPath)

# The following way to access a single nested Json Path works fine
Write-Host ("Nested JSON Version (via direct path) ==> " + $MyPSJson.NestedJson.Version)
# And THIS returns an empty line / is where I fall afoul of something in Powershell
Write-Host ("Nested JSON Version (via variable) ==> " + $MyPSJson.$NestedJsonPath)

# Other things I tried -- all returning an empty line / failing in effect
Write-Host ("Alternate Nested JSON Version ==> " + $($MyPSJson.$NestedJsonPath))
Write-Host ("Alternate Nested JSON Version ==> " + $MyPSJson.$($NestedJsonPath))
Write-Host ("Alternate Nested JSON Version ==> " + $($MyPSJson).$($NestedJsonPath))

# Similarly, while THIS works...
$MyPSJson | select-object -Property NestedJSON
# This will fail / return me nothing
$MyPSJson | select-object -Property NestedJSON.Version
。。。在做了一系列的研究之后,我发现了一个将其转换为哈希表的建议——但遗憾的是,这也有同样的问题。通过上面的代码片段,下面将JSON对象转换为哈希表

# Same problem with a hash-table if constructed from the JSON file...
[hashtable]$MyHash = @{}
# Populate $MyHash with the data from our quickie JSON file...
$QuickJson | get-member -MemberType NoteProperty | Where-Object{ -not [string]::IsNullOrEmpty($QuickJson."$($_.name)")} | ForEach-Object {$MyHash.add($_.name, $QuickJson."$($_.name)")}

# ... and even then -- $MyHash."$($NestedJsonPath)" -- fails, while a single level deep string works fine in the variable! :(
所以很明显,我遇到了Powershell内部逻辑问题,但我不能让Powershell对这一问题有太多帮助。添加一个'-debug'或类似的代码来增加冗长性并没有帮助阐明这一点

我怀疑这与本文()中提出的项目类似,但只是特定于变量

我也没有在Powershell语言规范中找到任何明显的东西(据我所知,3.0仍然是最新的--)。它可能在那里,我可能只是错过了它

任何关于如何让Powershell更好地使用它的建议都将不胜感激。我不确定Powershell如何/为什么可以使用简单字符串,但这里的“something.somethingelse”类型字符串似乎有问题

多谢各位

原件的进一步注释和补遗:

似乎有几个问题需要解决。一种是“处理单个嵌套级别”。这方面的“快速修复”似乎是使用“Invoke Expression”来解析语句,因此,例如(很重要-注意第一个变量的反勾号!):

调用表达式的使用也适用于多嵌套情况:

iex "`$MyPSJson.$LongNestedJsonPath"
提到的另一种方法是使用多个select语句。。。但我无法将其用于多嵌套对象(由于某些原因,Powershell似乎无法正确解析这些对象)

例如,在这个场景中:

($MyComp | select $_.DoubleNestedJSON | select FirstLevel)
Powershell返回

FirstLevel    
---------- 
。。。而不是实际的数据值。所以-就目前而言,由于Powershell显然无法解析多级嵌套对象,因此SELECT似乎无法处理这些对象?

为什么这不起作用 当您在字符串中提供所需的属性时,如下所示

[string]$NestedJsonPath = "NestedJson.Version"
Powershell查找名为
NestedJSon.Version
的属性。它实际上不是遍历属性,而是查找包含句点的字符串文字。事实上,如果我将这样的属性添加到JSON中,就像这样

[object]$QuickJson = @'
{
    "Name" : "I am a JSON",
    "Version" : "1.2.3.4",
    "SomeBool" : true,
    "NULLValue" : null,
    "ArrayOfVersions" : [1.0,2.0,3.0],
    "MyInteger" : 69,
    "NestedJSON.Version" : 69,
    "NestedJSON" : {
        "Version" : 5.0,
        "IsReady" : false
    }
}
我现在得到一个值,如下所示:

>$MyPSJson.$NestedJsonPath
69
获取值的最佳方法是使用两个单独的变量,如下所示

$NestedJson = "NestedJson"
$property   = "Version"

>$MyPSJson.$NestedJson.$property
5.0
或者,您也可以使用select语句,如下面的原始答案所示



如果您使用多个Select Object语句,它们将放弃其他属性,并允许您更轻松地向下钻取到所需的值。

这要归功于wOxxOm,因为它使事情走上了正确的轨道

调用表达式似乎在这种情况下非常有效(虽然有点昂贵,但在我个人的示例和情况中这很好),并且它可以处理多层次的嵌套

因此,作为上述代码片段的示例,以下内容将很好地解决(关键点-注意初始的回勾。这让我措手不及):

这将返回我们想要的结果:

Single level JSON test ==> 5.0
Double level JSON test ==> I am on the 2nd nested level

FoxDeploy使用多级选择的答案似乎不适用于2+级嵌套,不幸的是,出于一些奇怪的原因

使用:

($MyPSJson | select $_.DoubleNestedJSON | select FirstLevel)
我们从Powershell获得以下信息:

FirstLevel    
---------- 
。。。似乎Powershell没有完整解析嵌套对象?如果我们故意使用不存在的东西,我们会得到类似的结果:

($MyPSJson | select $_.DoubleNestedJSON | select Doesnotexist)
。。。也只是返回:

Doesnotexist  
------------ 
因此,就目前而言,“调用表达式”“似乎工作得最可靠(也最容易,因为这只是一个用路径字符串处理变量的例子)

到目前为止,我仍然无法解释这其中的原因(因为我很高兴地使用了“Dotwark”——通过数组处理多个变量),但至少现在有了一个解决方案。。。这就是调用表达式

到目前为止,我找到的调用表达式的最佳解释(最不坏的解释)如下(Microsoft自己对cmdlet的描述并没有很好地暗示它在这种情况下会有所帮助):


当你写这样的东西时

$MyPSJson.Name
这将尝试从对象
$MyPSJson
检索名为
Name
的成员。如果没有这样的成员,您将获得
$null

现在,当您使用变量作为成员名称时:

$MyPSJson.$ShortJsonPath
这与查找名为存储在
$ShortJsonPath
中的成员并检索其值的情况基本相同。这一点也不奇怪

当您尝试使用对象上不存在的成员时,例如

如前所述,您将获得
$null
操作符将仅访问作为其左侧表达式结果的确切对象的成员。它永远不会像您期望的那样通过成员层次结构。坦率地说,我不知道有哪种语言是这样工作的

它使用
Invoke Expressio的原因
Doesnotexist  
------------ 
$MyPSJson.Name
$MyPSJson.$ShortJsonPath
$MyPSJson.$NestedJsonPath
# equivalent to
# $MyPSJson.'NestedJSON.Version'
$MyPSJson.NestedJSON.Version
function Get-DeepProperty([object] $InputObject, [string] $Property) {
  $path = $Property -split '\.'
  $obj = $InputObject
  $path | %{ $obj = $obj.$_ }
  $obj
}

PS> Get-DeepProperty $MyPSJson NestedJson.Version
5,0
filter Get-DeepProperty([string] $Property) {
  $path = $Property -split '\.'
  $obj = $_
  $path | %{ $obj = $obj.$_ }
  $obj
}

PS> $MyPSJson | Get-DeepProperty nestedjson.version
5,0
filter Get-DeepProperty([string] $Property) {
$path = $Property -split '\.'
$obj = $_
    foreach($node in $path){
        if($node -match '.*\[\d*\]'){
            $keyPieces = $node -split ('\[')
            $arrayKey = $keyPieces[0]
            $arrayIndex = $keyPieces[1] -replace ('\]','')
            $obj = $obj.$arrayKey[$arrayIndex]
        } else { 
            $obj = $obj.$node 
        }
    }
$obj
}
$path = "nested.nestedtwo.steps[2]"
$payload | Get-DeepProperty $path
function getNestedJsonValue() {
    param(
        [Parameter(Mandatory = $true, ValueFromPipeline)] [PSCustomObject] $inputObj,
        [Parameter(Mandatory = $true)] [string] $valuePath
    )
    if (($valuePath -eq $null) -or ($valuePath.length -eq 0) -or ($inputObj -eq $null)) {
        return $inputObj
    }

    [System.Array] $nodes = "$valuePath" -split '\.'
    foreach ($node in $nodes) {
        if (($node -ne $null) -and ($node.length -gt 0) -and ($inputObj -ne $null)) {
            $inputObj = $inputObj.$node
        } else {
            return $inputObj
        }
    }
    return $inputObj
}