Loading file with array of hashtables failing - json

Leading on from a previous question, I've been using arrays and hashtables as variables to get values from nested data structures. e.g. Defining $fields and feeding it into a select operator:
$request = 'http://musicbrainz.org/ws/2/artist/5b11f4ce-a62d-471e-81fc-a69a8278c7da?inc=aliases&fmt=json'
$output = Invoke-WebRequest $request | ConvertFrom-Json
$fields = "name",
#{Name='begin'; Expression={$_.{life-span}.begin}},
#{Name='end'; Expression={$_.{life-span}.end}}
$output | select $fields
Giving me:
name begin end
---- ----- ---
Nirvana 1988-01 1994-04-05
However, if I defined the array as a JSON structure for importing (bandFields.json)
{
"fields": [
"name",
{
"Name": "begin",
"Expression": "{$_.{life-span}.begin}"
}, {
"Name": "end",
"Expression": "{$_.{life-span}.end}"
}
]
}
And try to import it a an array (similar to what I'd previously manually specified):
$request = 'http://musicbrainz.org/ws/2/artist/5b11f4ce-a62d-471e-81fc-a69a8278c7da?inc=aliases&fmt=json'
$output = Invoke-WebRequest $request | ConvertFrom-Json
$runConf = Get-Content -Raw -Path bandFields.json | ConvertFrom-Json
$fields = $runConf.fields
$output | select $fields
And I get this:
select : Cannot convert System.Management.Automation.PSObject to one of the following types {System.String, System.Management.Automation.ScriptBlock}.

Related

Reading a json file in key value pair in the same order that's given in input

I am writing a PowerShell Script, which will read a json file having different sections, like job1, job2 and so on.. Now my objective is to read each section separately and to loop through it as a key value pair. I also need to maintain the order of the input file, because the jobs are scheduled in sequence. and these jobs run taking the values from the json file as input.
I tried using Powershell version 5.1, in which I created PSCustomObject but the order is getting sorted alphabetically, which I DON'T want.
Json File :
{ "Job1": [
{
"Ram" : "India",
"Anthony" : "London",
"Elena" : "Zurich"
}],
"Job2": [
{
"Build" : "fail",
"Anthony" : "right",
"Sam" : "left"
}]}
$json = Get-Content -Path C:\PowershellScripts\config_File.json |
ConvertFrom-Json
$obj = $json.Job1
$json.Job1 | Get-Member -MemberType NoteProperty | ForEach-Object {
$key = $_.Name
$values = [PSCustomObject][ordered]#{Key = $key; Value = $obj."$key"}
$values
}
I am expecting to loop through each section separately and in the same order that's provided in the json file. For example looping through Job1 section and to fetch only the Values in the same order that's in the json file.
I will guarantee that this is not the best way to do this, but it works.
$json = Get-Content -Path C:\PowershellScripts\config_File.json |
ConvertFrom-Json
$out = ($json.Job1 | Format-List | Out-String).Trim() -replace "\s+(?=:)|(?<=:)\s+"
$out -split "\r?\n" | ForEach-Object {
[PSCustomObject]#{Key = $_.Split(":")[0]; Value = $_.Split(":")[1]}
}
Explanation:
The JSON object is first output using Format-List to produce the Property : Value format, which is piped to Out-String to make that output a single string. Trim() is used to remove surrounding white space.
The -replace removes all white space before and after : characters.
The -split \r?\n splits the single string into an array of lines. Each of those lines is then split by the : character (.Split(":")). The [0] index selects the string on the left side of the :. The [1] selects the string on the right side of the :.
Can you change the json schema?
I would probably make changes to the json schema before i tried to parse this (if possible of course).
Like this (changed only Job1):
$json = #"
{ "Job1": [
{
"Name": "Ram",
"Location" : "India"
},
{
"Name": "Anthony",
"Location": "London"
},
{
"Name": "Elena" ,
"Location": "Zurich"
}
],
"Job2": [
{
"Build" : "fail",
"Anthony" : "right",
"Sam" : "left"
}]}
"# | convertfrom-json
foreach ($obj in $json.Job1) {
$key = $obj.Name
$values = [PSCustomObject][ordered]#{Key = $key; Value = $obj."$key" }
$values
}

Powershell nested json to repeating csv

Kind of similar to Convert nested JSON array into separate columns in CSV file but instead of flattened csv (ie discipline_01, discicpline_01) exporting to multiple lines of the csv:
{
"data": [{
"attributes": {
"id": 10011,
"title": "Test procedure",
"slug": "slug",
"url": "http://test.test",
"disciplines": [
"discipline_a",
"discipline_b",
"discipline_c"
]
}
}]
}
export to
"id","title","slug","url","discipline"
"10011","Test procedure","slug","http://test.test","discipline_a"
"10011","Test procedure","slug","http://test.test","discipline_b"
"10011","Test procedure","slug","http://test.test","discipline_c"
Thanks to Export fields with nested values from JSON to CSV I've gotten this far:
$foo = invoke-restmethod $restquery -headers $headers
$foo |
select -ExpandProperty data |
select -ExpandProperty attributes |
select id, title, slug, url, disciplines |
foreach {
$_.disciplines = $_disciplines -join ' '
$_ |
export-csv c:\outfile.csv -notypeinformation
This gives me
"10011","Test procedure","slug","http://test.test","discipline_a discipline_b discipline_c"
But no clue how to get it to:
"id","title","slug","url","discipline"
"10011","Test procedure","slug","http://test.test","discipline_a"
"10011","Test procedure","slug","http://test.test","discipline_b"
"10011","Test procedure","slug","http://test.test","discipline_c"
Any help is appreciated.
While your posted Json is invalid,
this might do:
## Q:\Test\2019\05\31\SO_56401395.ps1
$foo = invoke-restmethod $restquery -headers $headers
$Data = $foo.data.attributes | ForEach-Object {
foreach ($discipline in $_.disciplines}(
[PSCustomObject]#{
id = $_.id
title = $_.title
slug = $_.slug
url = $_.url
discipline = $discipline
}
}
}
$Data | Export-Csv c:\outfile.csv -NoTypeInformation

How do I select specific data from a PowerShell object sourced from JSON data?

I have imported some JSON data and converted it to a PowerShell Object. I would like to understand how to retrieve specific portions of said data.
test.json:
{
"Table": {
"Users": {
"Columns": [ "[Id]",
"[FName]",
"[MName]",
"[SName]",
"[UName]",
"[Pasword]" ],
"data": "CustomUserData"
},
"Roles": {
"Columns": [ "[Id]",
"[Role]",
"[Description]" ],
"data": "CustomRoleData"
}
}
}
Import to PS Object:
$userdata = Get-Content .\test.json |ConvertFrom-Json
Retrieve and format column data:
PS> $userdata = Get-Content ./test.json |ConvertFrom-Json
PS> $columns = $userdata.Table.Users.Columns -join ","
PS> $columns
[Id],[FName],[MName],[SName],[UName],[Pasword]
Example retrieval of custom data:
PS> $userdata.Table.Users.data
CustomUserData
What I would like to do is:
Select just the table names. When I try and do this by calling $userdata.table I get the following:
PS> $userdata.Table |Format-List
Users : #{Columns=System.Object[]; data=CustomUserData}
Roles : #{Columns=System.Object[]; data=CustomRoleData}
What I am looking for is just a list of the table names, in this case - Users,Roles
I would also like to know how to leverage this to create a ForEach loop which cycles through each table name and prints the columns associated with each table - ultimately I will be using this to craft a SQL query.
Thank you!
Maybe this can help you.
It is a small function to output the property names recursively.
function Get-Properties($obj, [int]$level = 0) {
$spacer = " "*$level
$obj.PSObject.Properties | ForEach-Object {
$spacer + $_.Name
if ($_.Value -is [PSCustomObject]){
Get-Properties $_.Value ($level + 2)
}
}
}
In your case, you can use it like this:
$userdata = Get-Content ./test.json | ConvertFrom-Json
Get-Properties $userData
The console output will look like this:
Table
Users
Columns
data
Roles
Columns
data

How to pass variables into an escaped json string?

I'm trying to pass json into a REST api call in order to start builds on VSTS.
The body has two parameters that are passed in as an escaped string and i'm struggling to update the parameters before invoking my request. An example of my body is below:
$body ='
{
"definition":
{
"id": ""
},
"parameters": "{\"environment\":\"uat\", \"browser\": \"ie\"}"
}
'
This is passed into the following where I update the definition id successfully:
$bodyJson=$body | ConvertFrom-Json
$bodyjson.definition.id = $buildDefId
$bodyString=$bodyJson | ConvertTo-Json -Depth 100
This works successfully but I can't access the parameters element of the json in order to pass the browser and environments in as variables.
Do you have any suggestions on how to do this?
I have tried the following without success:
$params = $bodyJson.Paramaters
$params -replace "uat","test"
$bodyString=$bodyJson | ConvertTo-Json -Depth 100
This updates the parameter in $params but it isn't passed back into the json when converted. I feel that i'm close but obviously missing a step.
Apparently you have a Json (parameters) string embedded in another Json string.
Meaning that you have to ConvertFrom-Json twice to deserialize everything and ConvertTo-Json twice to serialize it back with the new parameters:
(Note that I swapped the variable names $body and $bodyJson because $body is an object and $bodyJson is actually your Json string)
$bodyJson = '
{
"definition": {
"id": ""
},
"parameters": "{\"environment\":\"uat\", \"browser\": \"ie\"}"
}
'
$body = $bodyJson | ConvertFrom - Json
$paramJson = $body.parameters
$parameters = $paramJson | ConvertFrom - Json
$parameters
environment browser
----------- -------
uat ie
Change the parameter:
$parameters.environment = "test"
And rebuild the Json string:
$paramJson = $parameters | ConvertTo-Json -Compress
$body.parameters = $paramJson
$bodyJson = $body | ConvertTo-Json
$bodyJson
{
"definition": {
"id": ""
},
"parameters": "{\"environment\":\"test\",\"browser\":\"ie\"}"
}

Pass one of the member as array for JSON in powershell

Here is a small Powershell code snippet:
$users = New-Object System.Collections.ArrayList
$userAsJson = '
{
"name" : "abc",
"companies" : ["facebook", "google"]
}'
$user = $userAsJson | ConvertFrom-Json
$null = $users.Add($user)
$users | ConvertTo-Json -Depth 5
It gives me the following expected output:
{
"name": "abc",
"companies": [
"facebook",
"google"
]
}
Now, I'm dynamically trying to create the companies list. I tried all possible things which I can think of. Here is what I have tried:
$company = New-Object System.Collections.ArrayList
$null = $company.Add('facebook')
$null = $company.Add('google')
$b = $company.ToArray()
$users = New-Object System.Collections.ArrayList
$userAsJson = '
{
"name" : "abc",
"companies" : $b
}'
$user = $userAsJson | ConvertFrom-Json
$null = $users.Add($user)
$users | ConvertTo-Json -Depth 5
Can anyone suggest me what is the best way to achieve it?
PowerShell's strength is in staying in the realm objects, until the time comes to interface with the outside world, such as when writing to a file or creating a string representation of these objects.
In your case that means:
# Array of companies; statically constructed here, but creating it
# dynamically works just as well.
$company = (
'facebook',
'google'
)
# Initialize the output collection.
# Note: Creating a [System.Collections.ArrayList] instance is
# advisable for building up *large* arrays *incrementally*.
# For smallish arrays, using regular PowerShell arrays will do; e.g.:
# $users = #() # initialize array
# $users += ... # append to array, but be aware that a *new* array
# is created behind the scenes every time.
$users = New-Object System.Collections.ArrayList
# Add a user based on the $company array defined above as
# a [pscustomobject]
$null = $users.Add(
[pscustomobject] #{
name = 'abc'
companies = $company
}
)
# After all users have been added *as objects*, convert them to JSON.
$users | ConvertTo-Json -Depth 5
The above yields (based on a single object having been added; with more, a JSON array would be output):
{
"name": "abc",
"companies": [
"facebook",
"google"
]
}