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

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"
}
}]
}

Related

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

Single value array being converted to String - 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.

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 }

Have PowerShell pass results to Pentaho

I have a PowerShell script that processes a json string. My goal is to have this pass a resultset to Pentaho so I can process it and put it in a database table.
My PowerShell script works as expected outside of Pentaho. I can parse the files and get the information I need without any issues. It's when I try to pass those values is when Pentaho returns goofy results.
Here is my script
$scriptMode = 'GetFileInfo'
$json = '{
"building": [
{
"buildingname": "NAPA Auto Parts",
"files": [{
"sheets": [{
"name": "BATTERY",
"results": [{
"filename": "BATTERY - 1679568711.xlsx",
"sku": "1679568711"
}
]
}
],
"name": "2.15.19.xlsx",
"status": "processed",
"fileId": "c586bba6-4382-42c4-9c29-bffc6f7fe0b6"
}, {
"name": "Oct-Nov 2018 11.30.18.xlsx",
"errors": ["Unknown sheet name: TOILET PLUNGER"],
"status": "failed",
"fileId": "afa7c43f-26dc-421c-b2eb-45ad1e899c42"
}
]
},
{
"buildingname": "O''Reily Auto Parts",
"files": [{
"sheets": [{
"name": "ALTERNATOR",
"results": [{
"filename": "ALTERNATOR - 6.3.19 1629453444.xlsx",
"sku": "1629453444"
}
]
}, {
"name": "OIL FILTER",
"results": [{
"filename": "OIL FILTER - 6.3.19 1629453444.xlsx",
"sku": "1629453444"
}
]
}
],
"name": "6.3.19.xlsx",
"status": "processed",
"fileId": "647089fe-9592-4e2b-984f-831c4acd4d9c"
}
]
}
]
}'
$psdata = ConvertFrom-Json -InputObject $json
IF ($scriptMode -eq "GetFileInfo") {
$psdata.building | foreach-Object {
foreach ($File in $_.files)
{
[PSCustomObject]#{
BuildingName = $_.buildingname
FileName = $File.name
fileId = $File.fileId
Status = $File.status}
}
}
}
ElseIF ($scriptMode -eq "GetErrorInfo") {
$psdata.building | foreach-Object {
foreach ($File in $_.files)
{
[PSCustomObject]#{
BuildingName = $_.buildingname
Errors = $File.errors
SheetName = $File.sheets.name
fileId = $File.fileId} | Where-Object {$_.errors -ne $null}
}
}
}
And here's how I have my transformation setup. I have a table input query that will set the run command for PowerShell based on what I want the script to do (either get file info or get error info).
Then I have the "Execute a process" step run the PowerShell command
This is what is returned in Pentaho vs what PowerShell returns
I'm expecting the results to be returned exactly as PowerShell returns them. I'm hoping I can accomplish this without exporting the data to another format. We have had nothing but issues with the Json Input step in Pentaho, so we chose PowerShell over the "Modified Javascript Value" step in Pentaho.
Any idea how I can get this to return a result set (like a SQL query would return) back to Pentaho?
Most likely your result set is returning the entire thing, just not "tabled" as you expected, it's probably returning the entire table all summed up in one long text format, but still having all the line breaks / column breaks.
Try using Split steps in your pentaho flow to work on the returned String. First off, try using a "Split field to rows" with the delimiter as "${line.separator}".
From there all you to do is pretty much split the whole thing until it is a table in pentaho.

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
}