ConvertTo-JSON from string - access JSON fields by name in PowerShell - json

How can I access a field like $body.uuid?
This is what I have tried:
$body = #"
{ "uuid": "Test07",
"subject": "Template07-Subject",
}
"#
$bodyJSON = ConvertTo-Json $body
Write-Host $bodyJSON
Write-Host "uuid=$($bodyJSON.uuid)"
Write-Host "uuid=$($bodyJSON.$uuid)"
Results:
"{ \"uuid\": \"Test07\",\r\n \"subject\": \"Template07-Subject\",\r\n}"
uuid=
uuid=

Your $body variable contains a JSON string.
Unless your intent is to embed that string in another JSON string, do not call ConvertTo-Json on it - the latter's purpose is to convert objects to JSON strings.
In order to parse the JSON string into an object (graph), pass it to ConvertFrom-Json, which returns [pscustomobject] instance(s).
You can use regular property access, such as .uuid on the resulting object(s).
Note:
As bluuf points out, your original JSON contains an extraneous , after the "subject" property, which makes it technically malformed - this has been corrected below.
Note, however, that ConvertTo-Json in PowerShell (Core) 7+ still accepts such JSON, whereas Windows PowerShell does not.
# Create a string containing JSON
$body = #"
{
"uuid": "Test07",
"subject": "Template07-Subject"
}
"#
# Parse the JSON string into a PowerShell object (graph).
$bodyAsObject = ConvertFrom-Json $body
# Now you can use property access.
$bodyAsObject.uuid # -> 'Test07'

Related

how to format json string for aws with powershell

i want to send an event to aws via a cli command in a powershell script. Here is the Json i need to send to the eventbridge:
[
{
"Sensor":"{\"id\":\"880/2021-04-13\",\"attributes\":\"green\",\"Name\":\"SensorGreen\",\"state\":\"SUCCEEDED\"}",
"Source":"google.com"
}
]
Thats what a tried in powershell:
$json='[{"Sensor":"{\"id\":\"880/2021-04-13\",\"attributes\":\"green\",\"Name\":\"SensorGreen\",\"state\":\"SUCCEEDED\"}","Source":"google.com"}]'|ConvertTo-Json -Compress
aws events put-events --entries $json --region "eu-central-1"
That does not work. I even tried to write it to a json file and read it and send it from the file but it doesnt work. It somehow leads to too many "\" slashes or no slashes for my "Sensor" where it is necessary. I even tried to create a object and just convert the Sensor object to Json and then the whole object again to have the escaping, but it is not working.
EDIT:
i tried also this as mentioned in the answer:
$RequestObject = [pscustomobject] #(#{
Sensor = [pscustomobject] #{
id = "880/2021-04-13"
attribute = "green"
Name = "SensorGreen"
state = "SUCCEEDED"
}
Source = "google.com"
})
$RequestObject.Get(0).Sensor=$RequestObject.Get(0).Sensor | ConvertTo-Json -Compress
$Json = $RequestObject | ConvertTo-Json -Compress
aws events put-events --entries $Json --region "eu-central-1"
i included the #() to have an array and converted the sensor object twice to json to include the slashes. but the result is:
Error parsing parameter '--entries': Invalid JSON: Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
if i use the command line interface only with the aws command and put the object directly into the command, then it works.. but the powershell does not.
You're passing a JSON string to an external program (aws)
Irrespective of how you constructed that string - directly as a string, or from an object / hashtable via ConvertTo-Json - as of PowerShell 7.1 - manual escaping of embedded " as \" is required, which, in turn requires escaping the preexisting \ preceding the embedded " as \\.
Note: None of this should be necessary, but due to a long-standing bug in PowerShell is - see this answer for details.
PowerShell Core 7.2.0-preview.5 now includes experimental feature PSNativeCommandArgumentPassing with an attempted fix, but, unfortunately, it looks like it will lack important accommodations for high-profile CLIs on Windows - see this summary from GitHub issue #15143. However, it would fix calls to aws.exe, the Amazon Web Services CLI.
# The JSON string to be passed as-is.
$json = #'
[
{
"Sensor":"{\"id\":\"880/2021-04-13\",\"attributes\":\"green\",\"Name\":\"SensorGreen\",\"state\":\"SUCCEEDED\"}",
"Source":"google.com"
}
]
'#
# Up to at least PowerShell 7.1:
# Manually perform the required escaping, to work around PowerShell's
# broken argument-passing to external programs.
# Note:
# -replace '\\', '\\' *looks* like a no-op, but replaces each '\' with '\\'
$jsonEscaped = $json -replace '\\', '\\' -replace '"', '\"'
# Now pass the *escaped* JSON string to the aws CLI:
aws events put-events --entries $jsonEscaped --region "eu-central-1"
ConvertTo-Json is for converting objects in PowerShell, not a string that you have tried to already write in Json. Your $Json variable produces this.
"[{\"Sensor\":\"{\\\"id\\\":\\\"880/2021-04-13\\\",\\\"attributes\\\":\\\"green\\\",\\\"Name\\\":\\\"SensorGreen\\\",\\\"state\\\":\\\"SUCCEEDED\\\"}\",\"Source\":\"google.com\"}]"
If you want to create the object in PowerShell and convert it to Json, then you can do this.
$RequestObject = [pscustomobject] #{
Sensor = [pscustomobject] #{
id = "880/2021-04-13"
attribute = "green"
Name = "SensorGreen"
state = "SUCCEEDED"
}
Source = "google.com"
}
$Json = $RequestObject | ConvertTo-Json -Compress
aws events put-events --entries $Json --region "eu-central-1"
Your Json will look like this if you print your variable out.
{"Sensor":{"id":"880/2021-04-13","attribute":"green","Name":"SensorGreen","state":"SUCCEEDED"},"Source":"google.com"}
Which I think is like the Json that the command is expecting. Not entirely sure why you need the strings escaping or the array. Here it is uncompressed.
{
"Sensor": {
"id": "880/2021-04-13",
"attribute": "green",
"Name": "SensorGreen",
"state": "SUCCEEDED"
},
"Source": "google.com"
}
Just noticed the powershell-2.0 tag. If you are using it, then you should do this instead to create your Json.
$Sensor = New-Object psobject -Property #{
id = "880/2021-04-13"
attribute = "green"
Name = "SensorGreen"
state = "SUCCEEDED"
}
$RequestObject = New-Object psobject -Property #{
Sensor = $Sensor
Source = "google.com"
}
$Json = $RequestObject | ConvertTo-Json -Compress
EDIT
If you absolutely must escape the strings in that way and have a single item array, then you should just pass the Json that you have written in your answer without any further conversion.
$json='[{"Sensor":"{\"id\":\"880/2021-04-13\",\"attributes\":\"green\",\"Name\":\"SensorGreen\",\"state\":\"SUCCEEDED\"}","Source":"google.com"}]'
If you want to make PowerShell do that for you then you would need to perform some string replacement on the Sensor object first.
PowerShell 2.0
$Sensor = New-Object psobject -Property #{
id = "880/2021-04-13"
attribute = "green"
Name = "SensorGreen"
state = "SUCCEEDED"
}
$SensorJson = $Sensor | ConvertTo-Json -Compress
$SensorJson.Replace("`"","\`"")
$RequestObject = New-Object psobject -Property #{
Sensor = $SensorJson
Source = "google.com"
}
$Json = $RequestObject | ConvertTo-Json -Compress
PowerShell 3.0+
$Sensor = [pscustomobject] #{
id = "880/2021-04-13"
attribute = "green"
Name = "SensorGreen"
state = "SUCCEEDED"
}
$SensorJson = $Sensor | ConvertTo-Json -Compress
$SensorJson.Replace("`"","\`"")
$RequestObject = [pscustomobject] #{
Sensor = $SensorJson
Source = "google.com"
}
$Json = $RequestObject | ConvertTo-Json -Compress
Then your AWS command
# Add the array around the compressed Json string.
aws events put-events --entries "[$Json]" --region "eu-central-1"
"[$Json]" prints
[{"Sensor":"{\"id\":\"880/2021-04-13\",\"attribute\":\"green\",\"Name\":\"SensorGreen\",\"state\":\"SUCCEEDED\"}","Source":"google.com"}]

PowerShell JSON string escape (backslash)

I need to HttpPost a Json-body to a ASP.NET Core Web Api endpoint (controller) using a PowerShell script.
$CurrentWindowsIdentity = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())
$CurrentPrincipalName = $CurrentWindowsIdentity.Identity.Name
# Build JSON payload
$JsonString = #"
{
"CurrentPrincipalName":"$CurrentPrincipalName"
}
"#
$response = Invoke-RestMethod -Uri "https://webapiendpoint.tld/api/somecontroller" -Method Post -Body $JsonString -ContentType "application/json"
Since the value of the variable $CurrentPrincipalName can be domain\username, the json get's invalid because of the backslash, which is not properly escaped.
Error in the log of the web api:
JSON input formatter threw an exception: 'C' is an invalid escapable character within a JSON string. The string should be correctly escaped. Path: $.CurrentPrincipalName | LineNumber: 15 | BytePositionInLine: 36.
System.Text.Json.JsonException: 'C' is an invalid escapable character within a JSON string. The string should be correctly escaped. Path: $.CurrentPrincipalName
How can i make sure that when creating a json string and adding variables - whose values of course can not be controlled - the json string gets properly escaped?
i also tried ConvertTo-Json like:
$JsonConverted = $JsonString | ConvertTo-Json
and then HttpPost that object, but that was even worse:
JSON input formatter threw an exception: The JSON value could not be converted to solutionname.model. Path: $ | LineNumber: 0 | BytePositionInLine: 758.
The robust way to create JSON text is to construct your data as a hash table (#{ ... }) or custom object ( [pscustomobject] #{ ... }) first and pipe to ConvertTo-Json:
$JsonString = #{
CurrentPrincipalName = $CurrentPrincipalName
} | ConvertTo-Json
That way, PowerShell performs any necessary escaping of values for you, notably including doubling the literal \ character in your $CurrentPrincipalName value to ensure that it is treated as a literal.
Note:
Depending on how deeply nested the hashtable is, you may have to add a -Depth argument to the ConvertTo-Json call to prevent more data from getting truncated - see this post for more information.
If you have multiple properties and want to preserve their definition order in the JSON representation, use an ordered hash table ([ordered] #{ ... }) or a custom object.

How to parse/access JSON returned by Invoke-WebRequest in Powershell 4

I have the following call to an API in a powershell script (Powershell 4.0):
$Json = Invoke-WebRequest -Uri $RequestURL -UseBasicParsing -Headers $headers -ContentType 'application/json; charset=utf-8' -Method POST -Body $postParams -TimeoutSec 40
...and the content of the response (which is a string in JSON format) is written to a file:
Set-Content $path -Value $Json.Content
An example of a typical response...
{
"MyArray": [{
"MyField": "A1",
"MyField2": "A2"
}, {
"MyField": "B1",
"MyField2": "B2"
}]
}
All well and good, but now I have a requirement to parse the returned content as JSON and query some properties from within this Powershell script.
I presume I need to convert my string to 'proper' JSON and then to a powershell object in order to access the properties...so I have tried combinations of ConvertTo-Json and ConvertFrom-Json but can't ever seem to access it in anything other than a string. For example...
$x = $Json.Content | ConvertTo-Json
Write-Host $x.MyArray[0].MyField
$y = $x | ConvertFrom-Json
Write-Host $y[0].MyArray[0].MyField
In both cases above I get an error "Cannot index into a null array" suggesting that MyArray is null.
How do I convert my $Json response object into an object I can drill down into?
See ConvertFrom-Json
Converts a JSON-formatted string to a custom object or a hash table.
The ConvertFrom-Json cmdlet converts a JavaScript Object Notation
(JSON) formatted string to a custom PSCustomObject object that has a
property for each field in the JSON string.
Once you get the response converted to custom object or a hash table, you can access the individual properties
The link includes coding examples
This seems to work...
[void][System.Reflection.Assembly]::LoadWithPartialName("System.Web.Extensions")
$x = (New-Object -TypeName System.Web.Script.Serialization.JavaScriptSerializer -Property #{MaxJsonLength=67108864}).DeserializeObject($Json.Content)
Write-Host $x.MyArray[0].MyField
...although not sure why yet.

Yet another loop JSON object using Powershell

I am not able to twist my head into understanding how to get Powershell to loop the entire JSON Structure, it wont' loop the System.Object[]
$x = ConvertFrom-Json '{
"Car companies": {
"Name of Company": "Ford",
"Cars": [{
"Name of car": "Ranger",
"Config": "Pickup"
},
{
"Name of car": "Puma",
"Config": "Hatchback"
}]
}
}'
foreach( $rootProperty in #($x.psobject.properties | where-object {$_.MemberType -eq "NoteProperty"}) ) {
write-host " - '$($rootProperty.Name)' = '$($rootProperty.Value)'"
foreach( $childProperty in #($rootProperty.Value.psobject.properties ) ) {
write-host "'$($childProperty.Name)' = '$($childProperty.Value)'"
}
}
Outut I get now is just
- 'Brand' = '#{Name of Brand=Ford; Cars=System.Object[]}'
Name of Brand' = 'Ford'
Cars' = ' '
...as a follop How to iterate through a unknown JSON data/object?
tl;dr
You're seeing a bug that unexpectedly string-expands the Cars property value's array elements to the empty string.
A simple workaround - for display purposes only - is to pipe the property value to Out-String to get the usual display representation:
"'$($childProperty.Name)' = '$($childProperty.Value | Out-String)'"
You're seeing a bug in how arrays of [pscustomobject] instances are stringified (as of PowerShell Core 7.0.0-preview.6):
Generally, PowerShell arrays are stringified by joining the stringified element representations with the separator specified in the $OFS preference variable, which defaults to a space char.
Normally, [pscustomobject] instances have a string representation that resembles a hashtable literal (but isn't one); e.g.:
PS> $custObj = [pscustomobject] #{ foo = 'bar' }; "$custObj"
#{foo=bar} # string representation that *resembles* a hashtable literal
Unexpectedly - and this is the bug - when custom objects are the elements of an array, they stringify to the empty string, which is what you saw:
PS> $custObj = [pscustomobject] #{ foo = 'bar' }; $arr = $custObj, $custObj; "[$arr]"
[ ] # !! Bug: custom objects stringified to empty strings, joined with a space
This is an indirect manifestation of a long-standing bug reported in this GitHub issue: that is, elements of an array being stringified are stringified by calls to their .ToString() method, and calling .ToString() on custom objects unexpectedly yields the empty string (unlike the string representation you get when you directly reference a single custom object in an expandable string, as shown above).

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