使用 PowerShell 深入到最低的 JSON 元素

问题描述 投票:0回答:2

我需要使用 ps 从 JSON 文件中提取一些信息。 JSON 具有嵌套的

components
数组,其中还可以包含
components
数组。

{
"name": "app",
"components": [
    {
        "component_name": "comp1",
        "component_packages": [
            "comp1_package1",
            "comp1_package2"
        ],
        "project_id": "1234",
        "file_path": "requirements_file",
        "ref": "%%VERSION%%",
        "components": [
            {
                "component_name": "comp1.1",
                "component_packages": [
                    "comp1.1_package1"
                ],
                "project_id": "2345",
                "file_path": "requirements_file",
                "ref": "%%VERSION%%",
                "components": [
                    {
                        "component_name": "comp1.1.1",
                        "component_packages": [
                            "comp1.1.1_package1"
                        ],
                        "project_id": "3456",
                        "file_path": "requirements_file",
                        "ref": "%%VERSION%%",
                        "components": []
                    }]
            },
            {
                "component_name": "comp1.2",
                "component_packages": [
                    "comp1.2_package1"
                ],
                "project_id": "4567",
                "file_path": "requirements_file",
                "ref": "%%VERSION%%",
                "components": []
            }
        ]
    },
    {
        "component_name": "comp2",
        "component_packages": [
            "comp2_package1",
            "comp2_package2"
        ],
        "project_id": "5678",
        "file_path": "requirements_file",
        "ref": "%%VERSION%%",
        "components": [
            {
                "component_name": "comp2.1",
                "component_packages": [
                    "comp2.1_package1"
                ],
                "project_id": "6789",
                "file_path": "requirements_file",
                "ref": "%%VERSION%%",
                "components": []
            }
        ]
    }
]   
}

对于

component
内的每个
components
,我需要执行一个脚本来收集更多信息,但我很难对所有元素一一进行迭代

我开始将 JSON 转换为 psobject (

Get-Content -Raw "$json_path" | ConvertFrom-Json
)

我不想修复 JSON 的深度。所以脚本应该具有适应性。

我尝试使用

while
循环

$comp = $object.components
while ( $comp -ne "" ) { 
   $comp | ForEach-Object {
       # to something
   }
}

但是这样不合适,因为即使我覆盖

$comp
,脚本也会忘记一些条目。

json powershell object-graph
2个回答
3
投票

只需使用一个函数并递归调用它

(注意:这可能不是最有效的方法)

$JsonObj = $Json | ConvertFrom-Json


function Drill-Json {
    [CmdletBinding()]
    param (
        [Parameter(
            Mandatory,
            Position = 0,
            ValueFromPipeline,
            ValueFromPipelineByPropertyName,
            ValueFromRemainingArguments
        )]
        [array]$JsonComponent 
    )
        
    
    
    $JsonComponent | ForEach-Object {
        <# 

            DO STUFF

        #>
        # $_.component_name
        if (($_.components.count)) {
            Drill-Json -JsonComponent $_.components
        }
    
    }
}

Drill-Json -JsonComponent $JsonObj.components

0
投票
如果涉及多个级别以及数组和哈希表的混合,那么处理

对象图(独立于源,如JsonYamlPowerShell本身)可能确实会变得相当复杂。这就是为什么我开始编写 ObjectGraphTools 模块,它可以帮助您探索相关对象,迭代节点,或者简单地查明特定节点(并更改其值)。

Install-Module -Name ObjectGraphTools $Object = $Json | ConvertFrom-Json # Where $Json holds the json string of your question
示例

列出

$Object.components节点下的所有节点:

$Object | Get-Node Components | Get-ChildNode

Path          Name Depth Value
----          ---- ----- -----
components[0]    0     2 @{component_name=comp1; component_packages=System.Object…
components[1]    1     2 @{component_name=comp2; component_packages=System.Object…

递归搜索所有 
Components

节点(您的具体问题):

$Object | Get-Node | Get-ChildNode -Recurse Components

Path                                                 Name       Depth Value
----                                                 ----       ----- -----
components                                           components     1 {@{component_…
components[0].components                             components     3 {@{component_…
components[0].components[0].components               components     5 {@{component_…
components[0].components[0].components[0].components components     7 {}
components[0].components[1].components               components     5 {}
components[1].components                             components     3 {@{component_…
components[1].components[0].components               components     5 {}

但事情可能还没有结束。
要获取

$Object.components
节点下的叶节点的完整列表:

$Object | Get-Node Components | Get-ChildNode -Leaf -Recurse

Path                                                            Name           Depth Value
----                                                            ----           ----- -----
...
components[0].components[0].ref                                 ref                5 %%VERSION%%
components[0].components[0].components[0].component_name        component_name     7 comp1.1.1
components[0].components[0].components[0].component_packages[0] 0                  8 comp1.1.1_package1
components[0].components[0].components[0].project_id            project_id         7 3456
components[0].components[0].components[0].file_path             file_path          7 requirements_file
components[0].components[0].components[0].ref                   ref                7 %%VERSION%%
components[0].components[1].component_name                      component_name     5 comp1.2
components[0].components[1].component_packages[0]               0                  6 comp1.2_package1
components[0].components[1].project_id                          project_id         5 4567
components[0].components[1].file_path                           file_path          5 requirements_file
components[0].components[1].ref                                 ref                5 %%VERSION%%
components[1].component_name                                    component_name
...

您可以使用特定路径属性来定位任何特定节点,例如:

$Object | Get-Node components[0].components[0].components[0].project_id Path Name Depth Value ---- ---- ----- ----- components[0].components[0].components[0].project_id project_id 7 3456

或使用 
Member-Access 枚举

: $Object | Get-Node components.components.components.project_id Path Name Depth Value ---- ---- ----- ----- components[0].components[0].components[0].project_id project_id 7 3456

您可能要去的地方是获取具有特定子节点的 
components

之一,并更改另一个子节点的值。

该语法还支持通配符,并具有一些“扩展点表示法 (Xdn)”运算符,可让您自由地定位深层节点。例如:

$Object | Get-Node ~project_id=3456 Path Name Depth Value ---- ---- ----- ----- components[0].components[0].components[0].project_id project_id 7 3456 例如更改

ref
component

product_id
3456
值,例如:
($Object | Get-Node ~project_id=3456..ref).Value = '1.2.3.4'
(确认结果:

$Object | ConvertTo-Json -Depth 9

说明:


~project_id

查找名为
    project_id
  •  的任何后代节点
    
    =3456 过滤结果节点,其中值等于
  • 3456
  • .. 选择父级
  • ref
    选择名为
  • ref
  • 的节点(
    project_id
    节点的兄弟节点) 节点的 Value 属性是对对象图中相关值的引用,因此可能用于修改原始对象。
© www.soinside.com 2019 - 2024. All rights reserved.