Gregory Suvalian
Gregory Suvalian

Reputation: 3842

How do I iterate through JSON array in powershell

How do I iterate through JSON array which is converted to PSCustomObject with ConvertFrom-JSON? Using foreach does not work.

$jsonArray ='[{"privateKeyLocation" : "C:\\ProgramData\\docker\\certs.d\\key.pem"},
{"publicKeyLocation" : "C:\\ProgramData\\docker\\certs.d\\cert.pem"},
{"publicKeyCALocation" : "C:\\ProgramData\\docker\\certs.d\\ca.pem"}]'
$json = convertfrom-json $jsonArray
$json | foreach {$_}

Returns

privateKeyLocation
------------------
C:\ProgramData\docker\certs.d\key.pem

Enumerator though says there are 3 members of array

>$json.Count
3

Upvotes: 5

Views: 12207

Answers (5)

Przemysław Ładyński
Przemysław Ładyński

Reputation: 1076

You can use ForEach-Object i.e:

$json | ForEach-Object -Process { Write-Hoste $_; }

That's I believe the simplest way and gives you easy access to properties if array contains objects with other properties.

Upvotes: 0

HLourenco
HLourenco

Reputation: 388

Most simple way, should be like this

    $ret ='[your json]'
    $ret | ConvertFrom-Json
    $data = $ret | ConvertFrom-Json

    foreach($data in $ret | ConvertFrom-Json) {
        Write-Host $data;
    }

Upvotes: 2

postanote
postanote

Reputation: 16116

Interesting enough. I responded to this exact question from the same OP on another forum. Though my response was just RegEx and be done with it, with no additional conversion.

Of course there are several ways to do this. The below is just what I came up with.
$jsonArray = '[{"privateKeyLocation" : "C:\\ProgramData\\docker\\certs.d\\key.pem"},
{"publicKeyLocation" : "C:\\ProgramData\\docker\\certs.d\\cert.pem"},
{"publicKeyCALocation" : "C:\\ProgramData\\docker\\certs.d\\ca.pem"}]'

([regex]::Matches($jsonArray,'(?<=\").:\\[^\"]+(?=\")').Value) -replace '\\\\','\' `
| ForEach { 
            If (Test-Path -Path $_)
            {"path $_ found"}
            Else {Write-Warning "Path $_ not found"}
            }


WARNING: Path C:\ProgramData\docker\certs.d\key.pem not found
WARNING: Path C:\ProgramData\docker\certs.d\cert.pem not found
WARNING: Path C:\ProgramData\docker\certs.d\ca.pem not found

So, maybe not as elegant as what was posted here, but it would get the OP where they wanted to be.

So, consolidating everything TheMadTechnician gave and what the OP is after, and attempting to make it as concise as possible, would give the OP the below (I added a element to show a positive response):

Clear-Host 
($jsonArray = @'
[{"privateKeyLocation" : "C:\\ProgramData\\docker\\certs.d\\key.pem"},
{"publicKeyLocation" : "C:\\ProgramData\\docker\\certs.d\\cert.pem"},
{"publicKeyCALocation" : "C:\\ProgramData\\docker\\certs.d\\ca.pem"},
{"publicKeyTestFileLocation" : "D:\\Temp\\test.txt"}]
'@ | ConvertFrom-Json | Format-List | Out-String) -split '[\r\n]+' -replace '(?m)^.+ : '`
| Where-Object {$_} | ForEach {
    If(Test-Path -Path $_){"The path $_ was found"}
    Else{Write-Warning -Message "The path $_ was not found}"}
}

WARNING: The path C:\ProgramData\docker\certs.d\key.pem was not found}
WARNING: The path C:\ProgramData\docker\certs.d\cert.pem was not found}
WARNING: The path C:\ProgramData\docker\certs.d\ca.pem was not found}
The path D:\Temp\test.txt was found

Which one is more to his liking is a matter of the OP choice of course.

The performance between the two varied on each test run, but the fastest time using the straight RegEx approach was:

Days              : 0
Hours             : 0
Minutes           : 0
Seconds           : 0
Milliseconds      : 43
Ticks             : 439652
TotalDays         : 5.08856481481481E-07
TotalHours        : 1.22125555555556E-05
TotalMinutes      : 0.000732753333333333
TotalSeconds      : 0.0439652
TotalMilliseconds : 43.9652

and the fastest on the consolidated version here was:

Days              : 0
Hours             : 0
Minutes           : 0
Seconds           : 0
Milliseconds      : 54
Ticks             : 547810
TotalDays         : 6.34039351851852E-07
TotalHours        : 1.52169444444444E-05
TotalMinutes      : 0.000913016666666667
TotalSeconds      : 0.054781
TotalMilliseconds : 54.781

Updating to add iRon's take on this topic

So this...

$jsonArray ='[{"privateKeyLocation" : "C:\\ProgramData\\docker\\certs.d\\key.pem"},
{"publicKeyLocation" : "C:\\ProgramData\\docker\\certs.d\\cert.pem"},
{"publicKeyCALocation" : "C:\\ProgramData\\docker\\certs.d\\ca.pem"}]'
$json = convertfrom-json $jsonArray

$json | ForEach {
    $Key = $_.psobject.properties.name; 
    "Testing for key " + $_.$Key
    Test-Path -Path $_.$Key
} 


Testing for key C:\ProgramData\docker\certs.d\key.pem
False
Testing for key C:\ProgramData\docker\certs.d\cert.pem
False
Testing for key C:\ProgramData\docker\certs.d\ca.pem
False

... and this:

('[{"privateKeyLocation" : "C:\\ProgramData\\docker\\certs.d\\key.pem"},
{"publicKeyLocation" : "C:\\ProgramData\\docker\\certs.d\\cert.pem"},
{"publicKeyCALocation" : "C:\\ProgramData\\docker\\certs.d\\ca.pem"}]' `
| convertfrom-json) | ForEach {
    $Key = $_.psobject.properties.name; 
    "Testing for key " + $_.$Key
    Test-Path -Path $_.$Key
} 

Testing for key C:\ProgramData\docker\certs.d\key.pem
False
Testing for key C:\ProgramData\docker\certs.d\cert.pem
False
Testing for key C:\ProgramData\docker\certs.d\ca.pem
False

Upvotes: 1

TheMadTechnician
TheMadTechnician

Reputation: 36332

The problem that you are having is not specific to it being a JSON array, it has to do with how custom objects in an array are displayed by default. The simplest answer is to pipe it to Format-List (or FL for short).

PS C:\Users\TMTech> $JSON|FL


privateKeyLocation : C:\ProgramData\docker\certs.d\key.pem

publicKeyLocation : C:\ProgramData\docker\certs.d\cert.pem

publicKeyCALocation : C:\ProgramData\docker\certs.d\ca.pem

Aside from that, when PowerShell outputs an array of objects it bases the columns that it displays upon the properties of the first object in the array. In your case that object has one property named 'privateKeyLocation', so that is the only column that appears, and since the other two objects do not have that property it does not display anything for them. If you want to keep it as a table you could gather all potential properties, and add them to the first item with null values, and that would allow you to display it as a table, but it still wouldn't look very good:

$json|%{$_.psobject.properties.name}|select -Unique|?{$_ -notin $json[0].psobject.Properties.Name}|%{Add-Member -InputObject $JSON[0] -NotePropertyName $_ -NotePropertyValue $null}

Then you can output as a table and get everything:

PS C:\Users\TMTech> $json

privateKeyLocation                    publicKeyLocation                      publicKeyCALocation                 
------------------                    -----------------                      -------------------                 
C:\ProgramData\docker\certs.d\key.pem                                                                            
                                      C:\ProgramData\docker\certs.d\cert.pem                                     
                                                                             C:\ProgramData\docker\certs.d\ca.pem

Edit: To get the value of each object in this case is tricky, because the property that you want to expand keeps changing for each object. There's two ways to do this that I can think of, what I would consider the right way, and then there's the easy way. The right way to do it would be to determine the property that you want to expand, and then reference that property directly:

$JSON |%{
    $PropName = $_.PSObject.Properties.Name
    $_.$PropName
}

That'll do what you want, but I think easier would be to pipe to Format-List, then Out-String, wrap the whole thing in parenthesis, split on new lines and replace everything up to : which should just leave you with the paths you want.

($JSON|FL|Out-String) -split '[\r\n]+' -replace '(?m)^.+ : '|?{$_}

Upvotes: 2

G42
G42

Reputation: 10019

You can index into the array. Check out $json.GetType()

$jsonArray ='[{"privateKeyLocation" : "C:\\ProgramData\\docker\\certs.d\\key.pem"},
{"publicKeyLocation" : "C:\\ProgramData\\docker\\certs.d\\cert.pem"},
{"publicKeyCALocation" : "C:\\ProgramData\\docker\\certs.d\\ca.pem"}]'
$json = convertfrom-json $jsonArray

foreach($i in 0..($json.Count-1)){
    $json[$i] | out-host
    $i++
}

Upvotes: 0

Related Questions