skip to Main Content

I need to extract some informations from a json file using ps. The json has nested components arrays wich can also contain a components array.

{
"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": []
            }
        ]
    }
]   

}

for each component inside components I need to execute a script to gather more information but I struggle with iterating to all the elements one by one.

I started to convert the json to a psobject (Get-Content -Raw "$json_path" | ConvertFrom-Json)

I dont want to fix the depth of the json. So the script should be adaptable.

I tried using a while loop

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

but like this it is not suitable, because even if I overwrite $comp, the script will forget some entries.

2

Answers


  1. Just use a Function and call it recursively

    (note: this might not be the most efficient way to do this)

    $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
    
    Login or Signup to reply.
  2. Handling object-graphs (independent of the source, like Json, Yaml or PowerShell itself) might indeed become pretty complex if it concerns several levels and a mixture of arrays and hash tables. That’s why I started to write the ObjectGraphTools module which might help you to explore the concerned object, iterate through the nodes, or simply pinpoint a specific node (and change its value).

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

    Examples

    List all the nodes under the $Object.components node:

    $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…
    

    Or get a full list of leaf nodes under the$Object.components node:

    $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
    ...
    

    You might target a specific node by using a specific path property, e.g.:

    $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
    

    Or using Member-Access enumeration:

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

    The syntax also supports wildcards and has some Extend Dot Notation (Xdn) operators which lets you freely target a deep node. E.g.:

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

    To change the ref value of the component that has a product_id of 3456, e.g.:

    ($Object | Get-Node ~project_id=3456..ref).Value = '1.2.3.4'
    

    (Confirm te results with: $Object | ConvertTo-Json -Depth 9)

    Explanation:

    • ~project_id finds any descendant node named project_id
    • =3456 filters the resulted node where the value equals 3456
    • .. selects the parent
    • ref selects the node named ref (the sibling of the project_id node)
    • The Value property of the node is a reference to the related value in the object-graph and might therefore might be used modify the original object.
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search