通过Powershell中的变量解析/访问嵌套JSON/哈希表数据时出现问题 [英] Problems parsing / accessing nested JSON / Hashtable data via variables in Powershell

查看:119
本文介绍了通过Powershell中的变量解析/访问嵌套JSON/哈希表数据时出现问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试动态解析&通过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

...在对此进行大量研究时,我遇到了一个将其转换为Hashtable的建议-可悲的是,它也存在相同的问题.因此,使用上面的代码片段,下面的代码会将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'或类似的东西以增加冗长性并没有帮助我们弄清这一点.

我怀疑这与本文中提出的内容类似( https://www.microsoft.com/en-usdownload/details.aspx?id=36389 ).它可能在那里,我可能会想念它.

任何有关如何使Powershell与其搭配使用的建议将不胜感激.我不确定Powershell如何/为什么使用简单的字符串就可以,但是在这里似乎与'something.somethingelse'类型的字符串有关.

谢谢.

更多说明和内容;原始文件的附录:

似乎有几个问题需要攻击.一种是处理单个嵌套级别".对此的快速修复"似乎正在使用"Invoke-Expression"来解析该语句,例如(重要-请注意带有第一个变量的反引号!):

iex "`$MyPSJson.$NestedJsonPath"

使用Invoke-Expression还可用于多嵌套情况:

iex "`$MyPSJson.$LongNestedJsonPath"

提到的另一种方法是使用多个select语句...但是我无法使它与多嵌套对象一起使用(Powershell出于某种原因似乎无法正确解析这些对象). /p>

例如,在这种情况下:

($MyComp | select $_.DoubleNestedJSON | select FirstLevel)

Powershell返回

FirstLevel    
---------- 

...而不是实际数据值.所以-目前看来,由于Powershell显然无法解决选择问题,因此selects不适用于多层嵌套对象吗?

解决方案

为什么这不起作用

在字符串中提供所需的属性时,就像这样

[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语句,如下面的原始答案所示.


$MyPSJson | select $_.NestedJSON | select Version
Version
-------
1.2.3.4

如果您使用多个Select-Object语句,它们将丢弃其他属性,并允许您更轻松地向下钻取您想要的值.

I'm trying to dynamically parse & build-up a data structure of some incoming JSON files I'm to be supplied with (that'll be in non-standard structure) via Powershell to then process the data in those files & hand them over to the next step.

As part of that, I'm trying to build up the data structure of the JSON file into essentially a list of of data-paths for me to parse through & grab the data out of, so that I can cope with arrays, nested JSON objects and so on. So far so good.

Where I fall into some sort of Powershell peculiarity is in handling 2+ levels of depth via a variable. Let me give you a nice code-block to demonstrate the problem...

# 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

... in doing a bunch of research around this, I came across a suggestion to transform this into a Hashtable -- but that has the same problem, sadly. So with the above code-snippet, the following will transform the JSON object into a hashtable.

# 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! :(

So it's pretty clear that I'm running into "something" of a Powershell internal logic problem, but I can't get Powershell to be overly helpful in WHY that is. Adding a '-debug' or similar in an attempt to increase verbosity hasn't helped shed light on this.

I suspect it's something akin to the items raised in this article here ( https://blogs.technet.microsoft.com/heyscriptingguy/2011/10/16/dealing-with-powershell-hash-table-quirks/ ) but just specific with variables.

I've not had any luck in finding anything obvious in the Powershell language specification (3.0 still being the latest from here as far as I can tell -- https://www.microsoft.com/en-usdownload/details.aspx?id=36389 ) either. It may be in there, I may just miss it.

Any advice in how to get Powershell to play nice with this would be greatly appreciated. I'm not sure how / why Powershell is fine with a simple string but seems to have issues with a 'something.somethingelse' type string here.

Thank you.

Further notes & addenda to the original:

It seems there are several issues to attack. One is "dealing with a single nested level". The "quick fix" for that seems to be using "Invoke-Expression" to resolve the statement, so for instance (IMPORTANT - take note of the back-tick with the first variable!):

iex "`$MyPSJson.$NestedJsonPath"

That use of Invoke-Expression also works with multi-nested situations:

iex "`$MyPSJson.$LongNestedJsonPath"

An alternative approach that was mentioned is the use of multiple select statements ... but I've not been able to get that to work with multi-nested objects (Powershell seems to not resolve those properly for some reason).

So for instance in this scenario:

($MyComp | select $_.DoubleNestedJSON | select FirstLevel)

Powershell returns

FirstLevel    
---------- 

... instead of the actual data value. So - for now, it seems that selects won't work with multi-level nested objects due to Powershell apparently not resolving them?

解决方案

Why this doesn't work

When you provide the properties that you'd like within a string, like this

[string]$NestedJsonPath = "NestedJson.Version"

Powershell looks for a property called NestedJSon.Version. It's not actually traversing the properties, but looking for a string literal which contains a period. In fact, if I add a property like that to your JSON like so.

[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
    }
}

I now get a value back, like so:

>$MyPSJson.$NestedJsonPath
69

The best way to get your values back is to use two separate variables, like this.

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

>$MyPSJson.$NestedJson.$property
5.0

Or, alternatively, you could use select statements, as seen in the original answer below.


$MyPSJson | select $_.NestedJSON | select Version
Version
-------
1.2.3.4

If you use multiple Select-Object statements, they'll discard the other properties and allow you to more easily drill down to the value you'd like.

这篇关于通过Powershell中的变量解析/访问嵌套JSON/哈希表数据时出现问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆