I'm trying to write some code in PowerShell v5.1 that recurses through a parsed JSON object to get its property names. An example JSON object is below:
{
"str1": "Hello World!",
"strings": {
"strA": "Foo!",
"strB": "Bar!"
},
"others": {
"myBool": true,
"myInt": 42
}
}
First I read and parse the object into a PSObject:
$jsonString = '{"str1": "Hello World!", "strings":{"strA": "Foo!","strB": "Bar!"}, "others":{"myBool": true,"myInt": 42}}'
$json = $jsonString | ConvertFrom-Json
Next I can use $json.PSobject.properties.name to get a list of all the top-level property names:
PS> $json.PSobject.properties.name
str1
strings
others
However, if I use $json.PSobject.properties.value, I only see the first two values output:
PS> $json.PSobject.properties.value
Hello World!
strA strB
---- ----
Foo! Bar!
I would expect to see the others property included here too. Is there a reason I'm not seeing it?
Related
I am new to Powershell and am having trouble doing something that I imagine may be pretty simple, but not having luck so far.
I have an array with two objects in it. It looks like this basically:
[
{
"name":"John",
"age":30,
...
...
},
{
"score":null,
"vehicle":"Camaro",
"engine":"V8",
...
...
}
]
My goal is to update the score value in the second object. I have had luck doing so when the key's value is already present as a String, but am not understanding why I am unable to get this to work when the key is present but the value is null (as shown above).
I have tried using a function which I learned about when doing a search for a previously posted question trying to do something similar:
Set Value of Nested Object Property by Name in PowerShell
The function from that question looks like this:
function SetValue($object, $key, $Value)
{
$p1,$p2 = $key.Split(".")
if($p2) { SetValue -object $object.$p1 -key $p2 -Value $Value }
else { $object.$p1 = $Value }
}
and can be called like this:
SetValue -object $Obj -key $Key -Value $Value
I am not sure why it matters if the value is NULL or not. It can find the key, but just not doing anything if its value is NULL.
My apologies if this already out there. Powershell is just a little different than anything I have worked with before! Any help is greatly appreciated! :)
The object generated by your JSON string is an array of two objects:
$json = #'
[
{
"name":"John",
"age":30,
},
{
"score":null,
"vehicle":"Camaro",
"engine":"V8"
},
]
'# | ConvertFrom-Json
$json[0] # => Element 0 of the Array
name age
---- ---
John 30
$json[1] # => Element 1 of the Array
score vehicle engine
----- ------- ------
Camaro V8
Updating the Value of the score property considering the JSON will always be the same, meaning, the element 1 of the array will always have that property would be as simple as:
$json[1].score = 'hello' # => Updating the property value
$json | ConvertTo-Json # => Converting the array back to Json
[
{
"name": "John",
"age": 30
},
{
"score": "hello",
"vehicle": "Camaro",
"engine": "V8"
}
]
On the other hand, if the arrangement of the objects wouldn't be always the same, you could loop over all elements of the array searching for that property and, once found, update it. For example:
# For each element of the array
foreach($object in $json) {
# if this object has a property with name `score`
# assign that `NoteProperty` to the variable `$prop`
if($prop = $object.PSObject.Properties.Item('score')) {
# update the Value of the `NoteProperty`
$prop.Value = 'hello'
}
}
$json | ConvertTo-Json # Convert it back
I am a newbie to Powershell and I couldn't find this on googling but how do I build a json structure with an array inside? Is it through a custom object? Have been a bit confused by the syntax that I have seen online and not sure what is the recommended way to do it. Eventually I need to be able to save it to a JSON file too.
json_ret = {
"a": 4,
"b": [ {"c" : 5, "d": "text", "e": "foo"}]
}
Yes you can build a json object through a PSCustomObject:
[PSCustomObject]#{
a = 4
b = #([ordered]#{
c = 5
d = "text"
e = "foo"
})
} | ConvertTo-Json
First we create PSObject using its type accelerator PSCustomObject.
Then we define the root key and value "a", and we have to create an array inside "b".
The #() statement creates an array, but we can't we create key-value pairs in array. So we use #{} to create hashtable. Before it [ordered] flag says the hashtable to keep the exact structure as we have created it.
Then we define the array values, and after that close the internal array-hashtable.
Now we end the PSCustomObject and pipe it ConvertTo-Json. Now you get a converted json.
Footnotes
If you want to dump the json to a file, then use this:
[PSCustomObject]#{
a = 4
b = #([ordered]#{
c = 5
d = "text"
e = "foo"
})
} | ConvertTo-Json | Out-File "Filepath"
If you want to save json to a variable:
$variable = ([PSCustomObject]#{
a = 4
b = #([ordered]#{
c = 5
d = "text"
e = "foo"
})
} | ConvertTo-Json)
If you want to create the JSON document directly, as a string, it's simplest to use a verbatim here-string:
$json_ret = #'
{
"a": 4,
"b": [ {"c" : 5, "d": "text", "e": "foo"}]
}
'#
You can easily save that to a file with $json_ret | Set-Content file.json, for instance.
By contrast, if you want to construct your data as an object graph first, to be converted to JSON with ConvertTo-Json later, see Wasif_Hasan's helpful answer.
As for what you tried:
An unquoted { ... } construct is a script block, which is a piece of PowerShell code for later invocation on demand - and the contents of your JSON document happen not to constitute valid PowerShell code, causing construction of the script block to fail.
If using variables, then can create body including array like shown below, where $text is the variable. No need to user ConvertTo-Json and can easily copy the body from postman directly.
$text = "ABC"
# Post Body
$body = #"
{
"name" = "$text",
"description" = "$text",
"myArray": [
{
"id": "2c91808680d3c34b0180dc81d78c21e9",
"type": "myType",
"name": "myName"
}
]
}
"#
$body
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).
Currently, I'm attempting to call upon an API to run a POST, with JSON data as the body. So I was wondering if anyone would be able to tell me how I need to format the text below inside the variable $postParams. I'm pretty new at working with JSON so I'm having so trouble with this.
Currently, I only have the following and don't know what to do about the second line on.
$postParams = #{name='Example'}
Here's is the entire data I was hoping to add to $postParams. So if you could help me with the 2nd, 4th, and 8th that'd be awesome. Thanks!
{
"name":"Example",
"template":{"name":"Template"},
"url":"http://localhost",
"page":{"name":"Landing Page"},
"smtp":{"name":"Sending Profile"},
"launch_date":"2019-10-08T17:20:00+00:00",
"send_by_date":null,
"groups":[{"name":"test group"}]
}
You'll need a here-string and ConvertFrom-Json.
here-string:
Quotation marks are also used to create a here-string. A here-string is a single-quoted or double-quoted string in which quotation marks are interpreted literally. A here-string can span multiple lines. All the lines in a here-string are interpreted as strings, even though they are not enclosed in quotation marks.
The resulting code:
# Use a PowerShell here string to take JSON as it is
$jsonString = #"
{
"name":"Example",
"template":{"name":"Template"},
"url":"http://localhost",
"page":{"name":"Landing Page"},
"smtp":{"name":"Sending Profile"},
"launch_date":"2019-10-08T17:20:00+00:00",
"send_by_date":null,
"groups":[{"name":"test group"}]
}
"#
# Pipe the string to create a new JSON object
$jsonObject = $jsonString | ConvertFrom-Json
# The resulting JSON object has properties matching the properties in the orig. JSON
$jsonObject.name
$jsonObject.url
# Nested property
$jsonObject.template.name
# Nested property in array
$jsonObject.groups[0].name
I've posted an online version of the above code at tio.run, so you can play around with it.
If you want to update several properties of the $jsonObject you can do the following:
$jsonObject.name = "NEW NAME"
$jsonObject.url = "NEW URL"
$jsonObject | ConvertTo-Json
ConvertTo-Json will take your object and create an appropriate JSON string:
{
"name": "NEW NAME",
"template": {
"name": "Template"
},
"url": "NEW URL",
"page": {
"name": "Landing Page"
},
"smtp": {
"name": "Sending Profile"
},
"launch_date": "2019-10-08T17:20:00+00:00",
"send_by_date": null,
"groups": [
{
"name": "test group"
}
]
}
If you $jsonObject has more than two levels of depth, use the -Depth parameter, otherwise not all object information will be included in the JSON string.
ConvertTo-Json:
-Depth
Specifies how many levels of contained objects are included in the JSON representation. The default value is 2.
Here is a tio.run link to a ConvertTo-Json example.
Hope that helps.
I can't test it currently, but try this.
$postParams = #'
{
"name":"Example",
"template":{"name":"Template"},
"url":"http://localhost",
"page":{"name":"Landing Page"},
"smtp":{"name":"Sending Profile"},
"launch_date":"2019-10-08T17:20:00+00:00",
"send_by_date":null,
"groups":[{"name":"test group"}]
}
'#
Make a hashtable, then convert to JSON:
$Hashtable = #{
Key1 = "Value1"
Key2 = "Value2"
}
$Json = $Hashtable | ConvertTo-Json
When trying to convert a JSON file via PowerShell:
$json = Get-Content "C:\folder1\test.txt"
$json | ConvertFrom-Json
write-output $json
I'm getting the following error:
invalid json primitive : [.
(system.argunment.exception)
I'm going out on a limb here, since you didn't provide your input data or the complete error message, but I guess that your problem is caused by a format mismatch between the output Get-Content provides and the input ConvertFrom-Json expects.
Get-Content reads the input file into an array of strings, whereas ConvertFrom-Json expects the JSON data in a single string. Also, piping $json into ConvertFrom-Json does not change the value of $json.
Change your code to the following and the error should disapear (provided there is no syntactical error in your input data):
$json = Get-Content 'C:\folder1\test.txt' | Out-String | ConvertFrom-Json
Write-Output $json
You should check your JSON input file for characters that are not properly escaped with a "\"
I have also seen this issue with an input JSON file that was incorrectly formatted as follows:
{
Object1
}
{
Object2
}
Corrected format:
[{
Object1
},
{
Object2
}]
Once the format was corrected, I had no more issues.
I was also receiving this error, and upon investigating my json file noticed that some of the JSON was invalid. I was ending the last object in an array with a comma like so:
[{ ..},]
Removing the comma fixed the issue for myself.
So in short, invalid JSON caused this issue for me.
You will get this error if your input data starts like this:
data: [
{
...
},
{
...
}
]
You need to remove data: (and only have [and ] in this example):
[
{
...
},
{
...
}
]