Parse JSON output into a CSV file using Powershell - 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

Related

PowerShell Replace value in JSON

In our Azure CICD Pipeline, we have an element where we are trying to deploy Policies. We have JSON file per policy in the repo and we bring all these json files together into one file as part of CI which later is deployed via the CD. The PowerShell wasn't written by me, but a Microsoft consultant who was on site a few years back.
The problem is that when all the JSON comes together, we get an illegal syntax e.g.
Altering the code to this works and deploys, but means we have to go through all our files manually replace [ with [[:
In summary the PowerShell bring all of this together, does some manipulation and outputs to a file in the artifacts folder.
This is just a small snippet of the json, but highlights the area and there are many areas like this in the total json that need replacing:
{
"functions": [
],
"variables": {
"location": "UK South"
},
"resources": [{
"properties": {
"displayName": "Allowed Locations for Resources",
"policyType": "Custom",
"mode": "Indexed",
"description": "description.",
"metadata": {
"version": "1.0.0",
"category": "General"
},
"parameters": {
"listOfAllowedLocations": {
"type": "Array",
"metadata": {
"description": "The list of locations that can be specified when deploying resources.",
"strongType": "location",
"displayName": "Allowed locations"
},
"allowedValues": [
"uksouth",
"ukwest"
],
"defaultValue": [
"uksouth",
"ukwest"
]
}
},
"policyRule": {
"if": {
"allOf": [{
"field": "location",
"notIn": "[parameters('listOfAllowedLocations')]"
},
{
"field": "location",
"notEquals": "global"
},
{
"field": "type",
"notEquals": "Microsoft.Resources/subscriptions/resourceGroups"
},
{
"field": "type",
"notEquals": "Microsoft.Resources/b2cDirectories"
}
]
},
"then": {
"effect": "audit"
}
}
},
"name": "Policy1",
"apiVersion": "2019-01-01",
"type": "Microsoft.Authorization/policyDefinitions",
"location": "[variables('location')]"
}]
}
My PowerShell is intro level at best, so I am struggling to get a replace working.
I can obtain the offending area and replace it in a Write-Host, but I don't know how to write the back to the originating object with without making a right mess of things:
if ($content.properties.policyRule.if.allOf -ne $null){
foreach ($param in $content.properties.policyRule.if.allOf){
Write-Host "were here..................."
#$param = ($param | ConvertTo-Json -Depth 100 | % { [System.Text.RegularExpressions.Regex]::Unescape($_) })
if ($param.notIn -ne $null){
$param.notIn.replace('[', '[[')
Write-Host $param.notIn
}
}
Any suggestions would be grateful.
The point is that the allOf node contains an array. Due to the member-access enumeration feature you will be able to conveniently read the notIn property but to write to it, you will need to be specific on the index ([0]) in the allOf node:
$Data = ConvertFrom-Json $Json # $Json contains your $Json snippet
$Data.resources.properties.policyRule.if.allOf[0].notIn = "[[parameters('listOfAllowedLocations')]"
$Data |ConvertTo-Json -Depth 9
In case you want to recursively find your items based on e.g. a specific name and value format from a specific property level, you might use this common reusable function to recursively find (and replace) a node in a complex PowerShell object:
function Get-Node {
[CmdletBinding()][OutputType([Object[]])] param(
[ScriptBlock]$Where,
[AllowNull()][Parameter(ValueFromPipeLine = $True, Mandatory = $True)]$InputObject,
[Int]$Depth = 10
)
process {
if ($_ -isnot [String] -and $Depth -gt 0) {
if ($_ -is [Collections.IDictionary]) {
if (& $Where) { $_ }
$_.get_Values() | Get-Node -Where $Where -Depth ($Depth - 1)
}
elseif ($_ -is [Collections.IEnumerable]) {
for ($i = 0; $i -lt $_.get_Count(); $i++) { $_[$i] | Get-Node -Where $Where -Depth ($Depth - 1) }
}
elseif ($Nodes = $_.PSObject.Properties.Where{ $_.MemberType -eq 'NoteProperty' }) {
$Nodes.ForEach{
if (& $Where) { $_ }
$_.Value | Get-Node -Where $Where -Depth ($Depth - 1)
}
}
}
}
}
Usage
Finding node(s) with a specific name and value (-format):
$Node = $Data.resources.properties.policyRule.if |Get-Node -Where {
$_.name -eq 'notIn' -and $_.value -Match "^\[\w+\('\w+'\)\]$"
}
$Node
Value : [parameters('listOfAllowedLocations')]
MemberType : NoteProperty
IsSettable : True
IsGettable : True
TypeNameOfValue : System.String
Name : notIn
IsInstance : True
Replacing the value of the found node(s):
$Node |ForEach-Object {
$_.Value = '[' + $_.Value
}
$Data |ConvertTo-Json -Depth 9
Results
{
"functions": [],
"variables": {
"location": "UK South"
},
"resources": [
{
"properties": {
"displayName": "Allowed Locations for Resources",
"policyType": "Custom",
"mode": "Indexed",
"description": "description.",
"metadata": {
"version": "1.0.0",
"category": "General"
},
"parameters": {
"listOfAllowedLocations": {
"type": "Array",
"metadata": {
"description": "The list of locations that can be specified when deploying resources.",
"strongType": "location",
"displayName": "Allowed locations"
},
"allowedValues": [
"uksouth",
"ukwest"
],
"defaultValue": [
"uksouth",
"ukwest"
]
}
},
"policyRule": {
"if": {
"allOf": [
{
"field": "location",
"notIn": "[[parameters('listOfAllowedLocations')]"
},
{
"field": "location",
"notEquals": "global"
},
{
"field": "type",
"notEquals": "Microsoft.Resources/subscriptions/resourceGroups"
},
{
"field": "type",
"notEquals": "Microsoft.Resources/b2cDirectories"
}
]
},
"then": {
"effect": "audit"
}
}
},
"name": "Policy1",
"apiVersion": "2019-01-01",
"type": "Microsoft.Authorization/policyDefinitions",
"location": "[variables('location')]"
}
]
}
Update 2022-11-21
Resolved an issue with $Null values in the Get-Node function, see also: PowerShell FilterScript error with some JSON Files (thanks
mklement0).

How to print the contents of the objects taking one value as input in powershell?

I need to display the contents related to each id number by taking id as input. The original format was in json, as below:
{
"ids": [
{
"id": "121100",
"Libraries": [
"cpa_sample_code_s.so",
"stv_test_code_s.so"
],
"Commands": [
"qaeMemInit",
"icp_sal_userStartMultiProcess(\"SSL\",CPA_FALSE)",
"rsaPerformanceTest(1,0x02,2,10,1000) [RSA API]"
],
"Label": "rsaPerformanceTest-Test"
},
{
"id": "121103",
"Libraries": [
"cpa_sample_code_s.so",
"stv_test_code_s.so"
],
"Commands": [
"qaeMemInit",
"icp_sal_userStartMultiProcess(\"SSL\",CPA_FALSE)",
"dhPerformanceTest(1,0x02,10,10000)"
],
"Label": "dhPerformanceTest-Test"
},
{
"id": "121202",
"Libraries": [
"cpa_sample_code_s.so",
"stv_test_code_s.so"
],
"Commands": [
"qaeMemInit",
"icp_sal_userStartMultiProcess(\"SSL\",CPA_FALSE)",
"runDcTestPerf(3,0,2,1,1,1,65536,1,100)"
],
"Label": "runDcTestPerf-Test"
}
]
}
I converted the above format from a json file to the below mentioned format in $myVar. My variable has a hash table but I am unable to display the values using $myvar["id"]. I am very new to powershell. Can anyone please help?
$myFile = get-content C:\Users\ssc\Desktop\powershell\activity.json
$myvar = $myFile | ConvertFrom-Json
PS C:\Windows\system32> $myvar
ids
---
{#{id=121100; Libraries=System.Object[]; Commands=System.Object[]; Label=rsaPerformanceTest-Test}, #{id=121103; Libraries=System.Object[]; Commands=System.Object[]; Label=dhPerformanceTest-Test}, #{id=121202; Libraries=System.Object[]; Commands=System.Object[]; Label=runDcTestPerf-Test}}
PS C:\Windows\system32>
$myvar.ids currently contains an array of objects - but you can populate your own hashtable, using the id property as the key, like this:
$myHashtable = #{}
$myvar.ids |ForEach-Object { $myHashtable[$_.id] = $_ }
At which point you should be able to resolve each by id:
PS ~> $myHashtable["121100"]
id Libraries Commands
-- --------- --------
121100 {cpa_sample_code_s.so, stv_test_code_s.so} {qaeMemInit, icp_sal_userStartMultiProcess("SSL",CPA_FALSE), ...}

delete a object from the json file using a powershell

In this Question i want to accomplish is that i am trying to delete a specific object in the json file.
But while doing so i am experiencing some difficulties i tried to refer article Iterate over JSON and remove JSON element in PowerShell
and implement the same but however it is deleting the entire element but i want to delete a specific object in the element not the entire element following are the required things
1. json file
{
"name": "JourneyPack",
"description": "Details of the journey across india",
"author": "Sachin",
"version": "1.0.0",
"main": "main.js",
"build": {
"applicationID": "desktop",
"Necessaryfiles": [
"main.js",
"package.json",
],
"Storage": {
"output": "./reunited"
},
"DeatilsOfJourney": [
{
"from": "../Pune",
"to": "../travel/Pune",
"filter": [
"**/*
]
},
{
"from": "../Delhi",
"to": "../travel/Delhi",
"filter": [
"**/*"
]
},
{
"from": "../Jharkhand",
"to": "../travel/Jharkhand",
"filter": [
"**/*"
]
},
],
"IOS": {
"category": "desktop"
},
"Windows": {
"icon": "images/desktopicons/icons.ico",
"target": [
"divfrieght"
],
"publisherName": [
"Sachin"
]
},
"divfrieght": {
"PointClick": true,
"standaloneMachine": true,
"allowrise": true,
"allowinstdir": true,
"menu": "JourneyPack"
}
},
"private": true,
}
following is the tried code again this is that i have referred from Iterate over JSON and remove JSON element in PowerShell
2. tried code
$inputFile = '<THE FULL PATH AND FILENAME TO YOUR JSON FILE>'
$outputFile = '<THE FULL PATH AND FILENAME FOR THE OUTPUT JSON FILE>'
$apijson = Get-Content -Path $inputFile -Raw | ConvertFrom-Json
# for safety, first make a copy of the original .paths object
$newPaths = $apijson.paths
foreach ($element in $newPaths.PSObject.Properties) {
$objName = $element.Name
$objValue = $element.Value
$objProperties = $objValue.PSObject.Properties
foreach ($prop in $objProperties) {
if ($prop.Value.'from' -eq 'Jharkhand') {
$propName = $prop.Name
$objProperties.Remove($propName)
Write-Host "Removed object $objName -- $propName"
}
}
}
# now overwrite the $apijson.paths with this cleaned up version
$apijson.paths = $newPaths
# I assume you want to convert it back to a .JSON file??
$apijson | ConvertTo-Json -Depth 100 | Set-Content -Path $outputFile -Force
i want to delete the object where "from" is equal to "../Jharkhand/"
Desired Output
{
"name": "JourneyPack",
"description": "Details of the journey across india",
"author": "Sachin",
"version": "1.0.0",
"main": "main.js",
"build": {
"applicationID": "desktop",
"Necessaryfiles": [
"main.js",
"package.json",
],
"Storage": {
"output": "./reunited"
},
"DeatilsOfJourney": [
{
"from": "../Pune",
"to": "../travel/Pune",
"filter": [
"**/*
]
},
{
"from": "../Delhi",
"to": "../travel/Delhi",
"filter": [
"**/*"
]
},
],
"IOS": {
"category": "desktop"
},
"Windows": {
"icon": "images/desktopicons/icons.ico",
"target": [
"divfrieght"
],
"publisherName": [
"Sachin"
]
},
"divfrieght": {
"PointClick": true,
"standaloneMachine": true,
"allowrise": true,
"allowinstdir": true,
"menu": "JourneyPack"
}
},
"private": true,
}
if anyone could help that would be really helpful
".paths" property does not belong to you json file, so removed this part of your script.
# for safety, first make a copy of the original .paths object
$newPaths = $apijson.paths
Try this code:
$inputFile = 'input.json'
$outputFile = 'output.json'
$apijson = Get-Content -Path $inputFile -Raw | ConvertFrom-Json
foreach ($element in $apijson.PSObject.Properties) {
$objName = $element.Name
$objValue = $element.Value
$objProperties = $objValue.PSObject.Properties
foreach ($prop in $objProperties) {
# Your object lies in this array
if ($prop.Name -eq 'DeatilsOfJourney') {
[System.Collections.ArrayList]$arr = $prop.Value
#Iterate over your array and find that object which you want to remove
for ($i = 0; $i -lt $arr.count; $i++) {
if ($arr[$i].'from' -eq '../Jharkhand')
{
$arr.RemoveAt($i)
$i--
}
}
$prop.Value = $arr
}
}
}
$apijson | ConvertTo-Json -Depth 100 | Set-Content -Path $outputFile -Force

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

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.