Using Powershell convert JSON with multiple column headers and rows - arrays

I've used this question to get me started. However, I have the following JSON data that has multiple column headers and rows that contain multiple sets of data.
{
"columnHeaders": {
"dimensions": [
"year_month",
"tag1",
"computer_name",
"account_name",
"resource_name",
"category3",
"tag8",
"tag11",
"db_engine"
],
"metrics": [
{
"name": "usage_amount",
"dataType": "float"
},
{
"name": "pretax_cost",
"dataType": "currency"
},
{
"name": "actial_cost",
"dataType": "currency"
}
]
},
"rows": [
{
"dimensions": [
"2022-04",
"(not set)",
"server1",
"account1",
"server1_bios_name",
"undefined",
"(not set)",
"(not set)",
"SQLServer"
],
"metrics": [
{
"count": "1",
"max": "0.52",
"min": "0.10",
"sum": "0.52"
},
{
"count": "1",
"max": "-22.24",
"min": "-22.24",
"sum": "-22.24"
},
{
"count": "1",
"max": "1.26",
"min": "0",
"sum": "1.52"
}
]
}
]
}
If I use the last suggestion, I can get the dimensions fine, but that I need to the "sum" value for each metric.
$A = Get-Content 'C:\Temp\sample.json' | ConvertFrom-Json
$Rows =
ForEach($Row in $A.rows )
{
$TmpHashCol = [Ordered]#{}
$TmpHashMet = [Ordered]#{}
For($i = 0; $i -lt $Row.dimensions.Length; ++$i )
{
$TmpHashCol.Add($A.columnHeaders.dimensions[$i],$Row.dimensions[$i])
#For($s = 0; $s -lt $Row.metrics.length; ++$s )
# {
$TmpHashMet.Add($A.columnHeaders.metrics.name[$i],$Row.metrics.sum[$i])
#$TmpHashMet.Add($A.columnHeaders.metrics[$i],$Row.metrics.sum[$i])
# }
}
For the '$TmpHashMet' I get the error: Exception calling "Add" with "2" argument(s): "Key cannot be null. Yet, when I try to specify the name it doesn't like that either.
This is what I would like for the data to look like when complete:
year_month : 2022-04
tag1 : (not set)
computer_name : server1
account_name : account1
resource_name : server1_bios_name
category3 : undefined
tag8 : (not set)
tag11 : (not set)
db_engine : SQLServer
usage_amount : 0.52
pretax_cost : -22.24
actial_cost : 1.52
Many thanks in advance!!!

This is one way you could do it, you need a new inner loop for the Values on .metrics.sum, I have also modified your code a bit so it works in case the Json comes with more than 1 row.
$headers = $json.columnHeaders.dimensions
$metrics = $json.columnHeaders.metrics.name
foreach($row in $json.rows) {
$out = [ordered]#{}
for($i = 0; $i -lt $row.dimensions.Count; $i++) {
$out[$headers[$i]] = $row.dimensions[$i]
}
for($i = 0; $i -lt $metrics.Count; $i++) {
$out[$metrics[$i]] = $row.metrics.sum[$i]
}
[pscustomobject] $out
}
The output should look like your desired one:
year_month : 2022-04
tag1 : (not set)
computer_name : server1
account_name : account1
resource_name : server1_bios_name
category3 : undefined
tag8 : (not set)
tag11 : (not set)
db_engine : SQLServer
usage_amount : 0.52
pretax_cost : -22.24
actial_cost : 1.52

Related

Powershell JSON : How to retrieve specific key-value pair for all array items

I am trying to get the list of "enabled" features from featureset array from below JSON snippet with powershell.
output expected :
feature1, feature2, feature4
I tried this piece of code but I was only able to get to the specific index of the array, not all elements, also not able to put condition for "enable" = true :
$output = foreach( $feature in $jsn.abc.comp1.featureset[0].psobject.Properties ) {
[PSCustomObject]#{
featureName = $feature.Name
featureValue = $feature.Value
}
}
json:
{
"abc": {
"comp1": {
"id": "1308",
"featureset":[
{
"name": "feature1",
"enable" : true,
"ID": "0670FF495355878281174937"
},
{
"name": "feature2",
"enable" : true,
"ID": "0670FF495355878281174937"
},
{
"name": "feature3",
"enable" : false,
"ID": "0670FF495355878281174937"
},
{
"name": "feature4",
"enable" : true,
"ID": "0670FF495355878281174937"
}
]
}
}
}
Loop over the whole featureset array, use Where-Object to filter:
$output = $jsn.abc.comp1.featureset |Where-Object enable -eq $true |ForEach-Object {
$_.Name
}

Powershell converting array to json yields unexpected results

I'm trying to dump a powershell array of hash tables to json like this:
$body= ,#($someArray | % {
{
#{
name = $_
}
}
})
$body | ConvertTo-Json
But this yields this result:
{
"value": [
{
"name": "test"
},
{
"name": "test-2"
}
],
"Count": 2
}
Not sure how to only get the value.
If I do ConvertTo-Json | $body, I get:
[
[
{
"name": "test",
},
{
"name": "test-2",
}
]
]
I'm doing the ,#() syntax because otherwise if I only have one object in my array it gets converted to an Hash Table instead of an array.
I am on Powershell 5.

PowerShell - print a JSON output with sorted array of objects?

How can I print a JSON output with sorted array of objects?
My $result object must remain as it is, the order of "Good" or "Bad" doesn't matter,
I'm trying to sort the objects in the arrays sorted by "count" property in ascending order.
My code:
$result = [PSCustomObject]#{
Good = #()
Bad = #()
}
$food = [PSCustomObject]#{
name = "Banana"
count = 2
}
if ($food.count -gt 3) { $result.Good += $food }
else { $result.Bad += $food }
$sortGood = $result.Good | Sort-Object count
$sortBad = $result.Bad | Sort-Object count
Write-Output ($result | ConvertTo-Json)
My JSON output is:
{
"Good": [
{
"name": "Apple"
"count": 10
},
{
"name": "Lime"
"count": 5
},
{
"name": "Peach"
"count": 7
}
],
"Bad": [
{
"name": "Banana"
"count": 2
},
{
"name": "Kiwi"
"count": 1
},
{
"name": "Orange"
"count": 3
}
]
}
How can I print a JSON that looks like this? (fruits sorted by "count" property in ascending order)
{
"Good": [
{
"name": "Lime"
"count": 5
},
{
"name": "Peach"
"count": 7
},
{
"name": "Apple"
"count": 10
},
],
"Bad": [
{
"name": "Kiwi"
"count": 1
},
{
"name": "Banana"
"count": 2
},
{
"name": "Orange"
"count": 3
}
]
}
[Problem fixed] Edited solution:
$result.Good = $result.Good | Sort-Object count
$result.Bad = $result.Bad | Sort-Object count
Write-Output ($result | ConvertTo-Json)
Sort-Object does not "sort the object". It returns a sorted copy of the object. So this
$sortGood = $result.Good | Sort-Object count
will result in $sortGood being sorted properly, and $result.Good being exactly as it was.
$json = #"
{
"Good": [
{"name": "Apple", "count": 10},
{"name": "Lime", "count": 5},
{"name": "Peach", "count": 7}
],
"Bad": [
{"name": "Kiwi", "count": 1},
{"name": "Orange", "count": 4}
]
}
"#
$data = ConvertFrom-Json $json
$food = #{
name = "Banana"
count = 2
}
if ($food.count -gt 3) {
$data.Good += $food
} else {
$data.Bad += $food
}
$data.Good = $data.Good | Sort-Object count
$data.Bad = $data.Bad | Sort-Object count
$result = $data | ConvertTo-Json -Depth 10
$result
gives
{
"Good": [
{
"name": "Lime",
"count": 5
},
{
"name": "Peach",
"count": 7
},
{
"name": "Apple",
"count": 10
}
],
"Bad": [
{
"name": "Kiwi",
"count": 1
},
{
"count": 2,
"name": "Banana"
},
{
"name": "Orange",
"count": 4
}
]
}
Note that I'm always re-assigning the values of $data.Good and $data.Bad:
Using $data.Good += $food creates a new array (!) with $food at the end, and then assigns that to $data.Good. (It's shorthand for $data.Good = $data.Good + $food.)
Using $data.Good = $data.Good | Sort-Object count creates a new array (!) in a different order, and then assigns that to $data.Good.
Hey there I guess you forgot to add -Property after Sort-Object
i.e
$sortGood = $result.Good | Sort-Object -Property count
Give it a try and let me know !!!
I would do it like this:
ConvertTo-Json #{
Good = $result.Good | sort Count
Bad = $result.Bad | sort Count
}

Change property name when converting Powershell object to JSON using ConvertTo-Json?

I have a dataset consisting of a two-dimensional array (flexData[5][2]). I have this defined in my Powershell script as follows:
class flexData {
[DateTime]$dateTime
[string]$firmwareVersion
[string[][]]$flexData
}
$flexObj = [flexData]#{dateTime = $(Get-Date); firmwareVersion = 'u031C'; flexData = #(#(0, 1), #(320, 17), #(45, 36), #(0, 0))}
The problem with this is that the output object that ConvertTo-Json spits out is hard to read:
{
"dateTime": "2021-10-11T13:58:25.0937842+02:00",
"firmwareVersion": "u031C",
"flexData": [
[
"0",
"1"
],
[
"320",
"17"
],
[
"45",
"36"
],
[
"0",
"0"
]
]
}
Is there a way to instead of using a single key name and two-dimensional arrays, to instead convert this to flexData0, flexData1 ... flexData4 and keep my actual data as single-dimensional arrays? I could obviously do this by manually defining my class as:
class flexData {
[DateTime]$dateTime
[string]$firmwareVersion
[string[]]$flexData0
[string[]]$flexData1
[string[]]$flexData2
[string[]]$flexData3
[string[]]$flexData4
}
But is there a smarter way of doing this? Especially since I would also like to make a third-dimension of my array to store multiple iterations of flexData?
You could add a constructor to your flexData class that creates an object from the top-level array instead:
class flexData {
[DateTime]$dateTime
[string]$firmwareVersion
[psobject]$flexData
flexData([DateTime]$dateTime, [string]$firmwareVersion, [string[][]]$flexData){
$this.dateTime = $dateTime
$this.firmwareVersion = $firmwareVersion
# Create object from nested array
$dataProperties = [ordered]#{}
for($i = 0; $i -lt $flexData.Length; $i++){
$dataProperties["$i"] = $flexData[$i]
}
$this.flexData = [pscustomobject]$dataProperties
}
}
Now, the individual outer array items will be listed as properties named 0 through (N-1):
PS ~> $data = [flexData]::new($(Get-Date), 'u031C', #(#(0, 1), #(320, 17), #(45, 36), #(0, 0)))
PS ~> $data |ConvertTo-Json
{
"dateTime": "2021-10-11T14:21:48.4026882+02:00",
"firmwareVersion": "u031C",
"flexData": {
"0": [
"0",
"1"
],
"1": [
"320",
"17"
],
"2": [
"45",
"36"
],
"3": [
"0",
"0"
]
}
}

I want to extract the data present in JSON file from a odata url but i am unable to extract the content using a loop

Below is the JSON file which i am referring..
{
"#odata.context": "https://ac.com/odata/$metadata#ModelVariableDataTypes(Id,ModelVariable,DoubleValues,ModelVariable(Id,Name,UnitOfMeasure,UnitOfMeasure(Name,Abbreviation)),DoubleValues(Id,Value,Timestamp,DataQuality,DataQuality(Id,Name)))",
"#odata.count": 1,
"value": [
{
"Id": 1928155,
"ModelVariable": {
"Id": 1929663,
"Name": "AccCore_CPULoadProcess",
"UnitOfMeasure": {
"Name": "%",
"Abbreviation": "%"
}
},
"DoubleValues": [
{
"Id": 75865549,
"Value": 0.0,
"Timestamp": "2018-09-25T03:35:00Z",
"DataQuality": {
"Id": 1,
"Name": "Good"
}
},
{
"Id": 75865729,
"Value": 0.0,
"Timestamp": "2018-09-25T03:40:00Z",
"DataQuality": {
"Id": 1,
"Name": "Good"
}
},
{
"Id": 75865873,
"Value": 0.0,
"Timestamp": "2018-09-25T03:45:00Z",
"DataQuality": {
"Id": 1,
"Name": "Good"
}
}
]
}
]
}
I want to extract the data present in JSON file from a odata url but i am unable to extract the content using a loop as in the JSON file the odata count is mentioned as 1(#odata.count": 1) so when i am trying to catch the entire data using a loop it is not working.
I want to extract the data present in the array field of doublevalues and wanted to show the output of the top three values of CPU process.
I am trying with the below code to extract the JSON data.
$path= "C:\Users\s.papolu\Desktop\mem.json"
$data = Get-Content -Path 'C:\Users\S.Papolu\Desktop\mem.json' | ConvertFrom-Json
$maxCount = $data.'#odata.count'
$maxCount
#"
for ($i = 0; $i -lt $maxCount; $i++)
{
$Name = $("{0:N1}" -f $data.value.ModelVariable.Name)
$cpu = $("{$i`:N2}" -f $data.value.DoubleValues.Value)
}
write-host $Name,$cpu
I'm not quite sure what you're asking here. For one thing, you've got an extraneous at-sign and double-quote in there.
MaxCount is just 1 here, so the loop only runs once. For each item, though, are you trying to store the results in some structure and then show it? Which of the DoubleValues do you want? The top three?
Is this closer to what you want?
$data = Get-Content -Path $path | ConvertFrom-Json
$maxCount = $data.'#odata.count'
$maxCount
$results = #()
for ($i = 0; $i -lt $maxCount; $i++)
{
# get the top three doubles values
$Doubles = #($data.value[$i].DoubleValues | sort -Property Value -Descending | select -first 3)
$results += [PsCustomObject]#{
Name = $data.value[$i].ModelVariable[0].Name;
Cpu = $Doubles;
}
}
$results | ft

Resources