Reputation: 3842
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
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
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
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
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
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