Single value array being converted to String - Powershell - powershell

I am converting this to JSON and sending in a POST request but the value of ContextTypes keeps getting converted to string.
$postParams = #{
instance=[PSCustomObject]#{
instanceId= $instanceId;
className="Permission";
schemaName="RBAC";
properties= #{
Name= $Name;
Description= $Description;
ServiceGPRId= 1;
CategoryId= 1;
ContextTypes=#('Object')
};
}
} | ConvertTo-Json
returns:
{
"instance": {
"instanceId": null,
"className": "Permission",
"schemaName": "RBAC",
"properties": {
"CategoryId": 1,
"ServiceGPRId": 1,
"Description": null,
"Name": null,
"ContextTypes": "Object"
}
}}
I have seen other answers and have tried (#(...)) as well, it doesn't work. For some reason, if I define the same name-value pair outside of the properties object, it works fine and returns:
{
"instance": {
"instanceId": null,
"className": "Permission",
"schemaName": "RBAC",
"ContextTypes": [
"Object"
],
"properties": {
"CategoryId": 1,
"ServiceGPRId": 1,
"Description": null,
"Name": null
}
}
}
I also tried converting it to Json using the -InputObject method, but that gives same results.
How do I make sure ContextTypes remains an array?

You need to add -Depth 3 to your ConvertTo-Json call to ensure that your object graph is serialized to its full depth:
#{
instance= [PSCustomObject] #{
instanceId= $instanceId
className="Permission"
schemaName="RBAC"
properties= [PSCustomObject] #{
Name= $Name
Description= $Description
ServiceGPRId = 1
CategoryId = 1
ContextTypes = #('Object')
}
}
} | ConvertTo-Json -Depth 3
Unfortunately, -Depth defaults to 2, which explains your - subtle - symptom: essentially, your array was serialized as its string-expanded value, which yielded just its (only) element (e.g., "$(#('foo'))" yields 'foo').
For background information, see:
this post.
this GitHub issue.

Related

How to Reduce Array to dynamic object in Powershell

I have a JSON array like this:
$json = [
{ "name": "Name", "value": "Mike" },
{ "name": "Age", "value": 25 },
{ "name": "IsMarried", "value": true }
]
Expected output is this:
{
"Name": "Mike",
"Age": 25,
"IsMarried": true
}
In javascript I would do it this way:
const result = json.reduce((acc, { name, value }) => { acc[name] = value; return acc; }, {})
Question:
Is there an existing function like reduce? How can I achieve same effect?
To 'merge' array items like that, I would use an ordered Hashtable like below:
$json = #'
[
{ "name": "Name", "value": "Mike" },
{ "name": "Age", "value": 25 },
{ "name": "IsMarried", "value": true }
]
'#
# create an ordered Hashtable to store the values
$combine = [ordered]#{}
($json | ConvertFrom-Json) | ForEach-Object {
$combine[$_.Name] = $_.Value
}
# now you can leave it as Hashtable and convert it to JSON
$combine | ConvertTo-Json
# or you can convert (cast) to PsCustomObject first:
# [PsCustomObject]$combine | ConvertTo-Json
Result:
{
"Name": "Mike",
"Age": 25,
"IsMarried": true
}

Parse JSON output into a CSV file using Powershell

I'm using PowerShell to extract data via an API and would like to parse the JSON into a CSV file. How would I parse each of the JSON results into a CSV structure like this:
$Date, $app, $pagename, $range1, $range1_value
$Date, $app, $pagename, $range2, $range2_value
$Date, $app, $pagename, $range3, $range3_value
The JSON looks like this:
{
"fields": [
{
"label": "app",
"field": "app",
"type": "string"
},
{
"label": "pagename",
"field": "pagename",
"type": "string"
},
{
"label": "range1",
"field": "count(*)",
"type": "integer",
"aggregation": "filter"
},
{
"label": "range2",
"field": "count(*)",
"type": "integer",
"aggregation": "filter"
},
{
"label": "range3",
"field": "count(*)",
"type": "integer",
"aggregation": "filter"
}
],
"results": [
[
"application1",
"loginpage",
41425,
41266,
18869
],
[
"application2",
"loginpage",
7424,
7113,
2905
]
],
"moreData": false,
"schema": "record"
}
I've tried various methods (e.g. Convertto-JSON and Convertfrom-JSON) but I don't seem to be able to connect the 'fields' and 'results' together into a hashtable. I was hoping I could create it as a $JSON object and then iterate through each result like $JSON[0..1].
Let's start by parsing your input data!
Use a for loop to iterate over the individual array items in the results values, then use the index to resolve the type and label name from the fields list:
# Convert from json
$data = $jsonString |ConvertFrom-Json
# Set up a type table for easy conversion
$typeTable = #{'integer' = [int]}
# Iterate over each row in the results
$results = foreach($values in $data.results){
# Create dictionary to hold property values for the row
$Properties = [ordered]#{}
for($index = 0; $index -lt $data.fields.Count; $index++){
# Resolve field metadata by index
$field = $data.fields[$index]
# Take type mappings into account and write to $Properties dictionary
if($typeTable.ContainsKey($field.type)){
$Properties[$field.label] = $values[$index] -as $typeTable[$field.type]
}
else{
$Properties[$field.label] = $values[$index]
}
}
# Output structured object
[PSCustomObject]$Properties
}
Now that we have nice objects we can work with, we can use Select-Object and Export-Csv to create the desired output format:
$results |Select-Object #{Name='Date';Expression={Get-Date -Format yyyyMMdd}},app,pagename,#{Name='2000';Expression={'2000'}},range3 |Export-Csv -Path .\path\to\output.csv -NoTypeInformation

Building a nested dictionary/hash tables in powershell displays "System.Collections.Hashtable"

I'm trying to create a body for a webrequest which is in the form of a nested dictionary.
$body +=#{}
$body["tables"] = #()
$body["tables"] += #{}
$body["tables"][0]["id"] += #{}
$body["tables"][0]["id"]["columnId"] = "1"
$body["tables"][0]["id"]["fieldType"] = "1"
$body["tables"][0]["textFilter"] = #{"value" = "123"}
$body2Json = ConvertTo-Json $body
When I try to print this, I get the following:
{
"tables": [
{
"id": "System.Collections.Hashtable",
"textFilter": "System.Collections.Hashtable"
}
]
}
Not sure what am I doing wrong here, still new to powershell
You created a pretty complex, multi-node PowerShell object, but the ConvertTo-Json cmdlet only converts the first two levels of depth before it stops.
Fortunately, You can control this behavior with the -Depth parameter like so:
ConvertTo-Json $body -Depth 5
{
"tables": [{
"id": {
"columnId": "1",
"fieldType": "1"
},
"textFilter": {
"value": "123"
}
}]
}

Read json value from the http response trigger by invoke-webrequest

I am trying to fetch a particular value from the JSON response of an invoke-web request. But the value is not capturing
Tried using the following script, where the $body contains the response.
$url = "http://localhost:9096/getMachineStatus"
$HTTP_Request = [System.Net.WebRequest]::Create($url)
$HTTP_Response = $HTTP_Request.GetResponse()
$HTTP_Status = [int]$HTTP_Response.StatusCode
$body = Invoke-WebRequest -Uri $url
The response of the above script:
{
"Name": "LocalTestMachine",
"Profile": "QA",
"Stacks": [
{
"Region": "Mumbai-1",
"State": "Stopped",
"StackName": "QA",
"StackCreationStatus": "CREATE_Success",
"Instances": [
{
"MachineName": "LocalMachine",
"IpAddress": "10.10.10.164",
"State": "stopped",
"InstanceId": "i-0777e90151b22da44",
"ImageId": "ami-0322ff2d8d099g56c",
"CustomImageName": "ubuntu-trusty-16.04",
"InstanceType": "m4.large",
"LaunchTime": "2019-09-04T02:42:36-04:00",
"AvailabilityZone": "Mumbai-1",
"Tags": [
{
"Key": "ProductLine",
"Value": "Cloud"
}]
}]
}]
}
I just want to retrieve the value associated with the object State which is Stopped.
I tried with
$currentVMState = $body | where {$_.State}
It is not working
To get the value of the first State item in the Stacks array, do this:
$json = $body | ConvertFrom-Json
$json.Stacks[0].State
returns
Stopped
First you need to convert the response to json:
$json = $body | ConvertFrom-Json
Then iterate the $json object to get the state value:
$json.stacks.instances | ForEach-Object { $_.State }

Loop through Json in powershell

I have json that I get from an API that returns a value like this
[{
"Location": "/xxx/005D2"
}, {
"Location": "/xxx/020D2"
}, {
"Location": "/xxx/061D2"
}, {
"Location": "/xxx/086D2"
}, {
"Location": "/xxx/100D2"
}]
When I call the URL and access the variable
$installs= Invoke-RestMethod -Uri $installLocation -Method Get;
I get the following
Location
--------------
/xxx/100D2
/xxx/120D2
/xxx/110D2
etc
How can I loop through these so I only access 1 location at a time?
You acutally don't want to loop through JSON, but through a PowerShell object ($installs). You can do that, as with any other PowerShell object.
$installs | ForEach-Object {
$_.Location
}